Springboot整合shiro

导入依赖

XML 复制代码
   <!--    引入springboot的web项目的依赖        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
​
<!--    shiro    -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.12.0</version>
        </dependency>

配置类

java 复制代码
package com.qf.shiro2302.config;
​
import com.qf.shiro2302.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
import java.util.Arrays;
import java.util.List;
​
@Configuration
@Slf4j
public class ShiroConfig {
​
​
​
    @Bean
    public Realm realm(){
        AuthorizingRealm authorizingRealm = new AuthorizingRealm() {
​
            /**
             *
             * @param token 这的token就是调用login方法时,传入的token对象
             * @return AuthenticationInfo 对象中,封装了用户的身份信息(principals),密码(credentials),提供信息的realm的名字
             * @throws AuthenticationException
             */
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                System.out.println("===========获取身份信息===============");
                //查询数据库获取当前用户名对应的User对象
                String username = (String) token.getPrincipal();
                System.out.println("username="+username);
                System.out.println("this.getName()={}"+this.getName());
                User user=getUserFromDB(username);
​
                //需要返回shiro规定的AuthenticationInfo类型的对象
                //这个对象中,包含了用户的身份认证信息
                // SimpleAuthenticationInfo的构造函数中的第一个参数,principals代表用户的身份信息
                // 一般可以使用 user对象,或者使用用户名也可以
                // 第三个参数,代表当前realm的名字,固定写法
                //Authentication 证明的意思
                SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
​
                return authenticationInfo;
            }
​
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                System.out.println("============获取授权===============");
​
                // 从数据库表中查询当前用户具有的角色和权限字符串
                User user = (User) principalCollection.getPrimaryPrincipal();
​
                List<String> roles= getRolesFromDB(user);
                List<String> permissions= getPermissionFromDB(user);
​
                // 按照约定返回AuthorizationInfo对象
                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                // 放入从数据库中查询到的当前用户的角色信息
//                authorizationInfo.addRole("test");
                authorizationInfo.addRoles(roles);
​
                // 放入从数据库中查询到的当前用户的权限信息
//             authorizationInfo.addStringPermission("document:read");
                authorizationInfo.addStringPermissions(permissions);
                return authorizationInfo;
            }
        };
​
​
        return authorizingRealm;
​
​
    }
​
    private List<String> getRolesFromDB(User user) {
        return Arrays.asList("test","admin");
    }
​
    private List<String> getPermissionFromDB(User user) {
        return Arrays.asList("document:read","document:write");
    }
​
    private User getUserFromDB(String username) {
        User user = new User(100, "数据库用户", "123456", "123@qq.com");
        return user;
    }
​
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        //ShiroFilterChainDefinition 此接口就一个实现类  默认Shiro过滤器链定义类
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
​
        // 让登录接口直接被shiro的过滤器放行
        //第一个参数:接口的路径
        //第二个参数:shiro内部的过滤器的名字,anon代表无需登录即可访问的特殊的过滤器,注意过滤器的名字是固定的,不能乱写
        chainDefinition.addPathDefinition("/login/dologin", "anon");
        // 放行springboot的错误页面的请求url  这个页面就是个response拼接的页面
        chainDefinition.addPathDefinition("/error", "anon");
​
​
        //增加角色或者权限,对于某些请求
        // logged in users with the 'test' role
//        chainDefinition.addPathDefinition("/test/**", "authc, roles[test,admin], perms[document:read,document:write]");  这个如果改成anon,就不能加后面的角色或者权限,否则,将会被认为需要登录,重定向到登录页
​
        // all other paths require a logged in user
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }
}

调用接口

java 复制代码
package com.qf.shiro2302.controller;
​
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/login")
@Slf4j
public class LoginController {
​
    @PostMapping("/dologin")
    public String dologin(String username,String password){
​
        //使用Shiro进行登录处理
        Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
       //为了调用shiro的登录方法,需要准备一个Token对象
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        System.out.println(token);
        subject.login(token);//使用shiro登录流程
​
        return "登陆成功 ";
​
​
    }
​
}

获取ShiroSession中的用户,注解添加权限

java 复制代码
package com.qf.shiro2302.controller;
​
import com.qf.shiro2302.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/test")
public class TestController {
​
​
    @RequiresRoles({"test","admin"})
    @RequiresPermissions({"document:read","document:write"})
    @RequestMapping("/test1")
    public String hello1(){
        return "hello shiro !!!";
    }
​
​
    @RequestMapping("/test2")
    public User hello2(){
        //如果使用shiro获取当前登录用户的身份信息
        Subject subject = SecurityUtils.getSubject();
        User principal = (User) subject.getPrincipal();
        System.out.println(principal);
        return principal;
    }
}
XML 复制代码
shiro:
  loginUrl: /login.html
  #配置没登陆的时候重定向的页面。这个请求是可以放行的

优化整合shiro,登录密码MD5Hash加密,内置处理

1.配置类

java 复制代码
package com.qf.shiroHomework.config;
​
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.shiroHomework.entity.*;
import com.qf.shiroHomework.mapper.TPersMapper;
import com.qf.shiroHomework.mapper.TRoleMapper;
import com.qf.shiroHomework.mapper.TRolePermsMapper;
import com.qf.shiroHomework.mapper.TUserRoleMapper;
import com.qf.shiroHomework.service.ITUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
​
@Configuration
@Slf4j
public class ShiroConfig {
​
    @Autowired
    private ITUserService itUserService;
    @Autowired
    private TUserRoleMapper tUserRoleMapper;
    @Autowired
    private TRolePermsMapper tRolePermsMapper;
    @Autowired
    private TPersMapper tPersMapper;
    @Autowired
    private TRoleMapper tRoleMapper;
​
    //将Realm对象放入IOC容器里
    @Bean
    public Realm realm(){
        AuthorizingRealm authorizingRealm = new AuthorizingRealm() {
​
            /**
             *
             * @param token 这的token就是调用login方法时,传入的token对象
             * @return AuthenticationInfo 对象中,封装了用户的身份信息(principals),密码(credentials),提供信息的realm的名字
             * @throws AuthenticationException
             */
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                System.out.println("===========获取身份信息===============");
                //获取身份信息
                String username = (String) token.getPrincipal();
​
                QueryWrapper<TUser> wrapper = new QueryWrapper<>();
                wrapper.eq("username",username);
                TUser user = itUserService.getOne(wrapper);
​
                // 需要返回shiro规定的AuthenticationInfo类型的对象
                // 这个对象中,包含了用户的身份认证信息
                // SimpleAuthenticationInfo的构造函数中的第一个参数,principals代表用户的身份信息
                // 一般可以使用 user对象,或者使用用户名也可以
                // 第三个参数: 盐
                // 第四个参数,代表当前realm的名字,就是一个标识,标识是这个Bean调用的这个方法,没有作用,固定写法
​
                SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
​
                return authenticationInfo;
            }
​
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                TUser user = (TUser) principalCollection.getPrimaryPrincipal();
                SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
                log.info("用户角色={}",getRolesFromDB(user));
                log.info("用户权限={}",getPermissionFromDB(user));
                authorizationInfo.addRoles(getRolesFromDB(user));
                authorizationInfo.addStringPermissions(getPermissionFromDB(user));
                return authorizationInfo;
            }
        };
​
        // 把HashedCredentialsMatcher对象设置到authorizingRealm对象中
        authorizingRealm.setCredentialsMatcher(hashedCredentialsMatcher());
​
        return authorizingRealm;
    }
​
​
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
​
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
      //设置算法名称
        matcher.setHashAlgorithmName("md5");
        //设置hash次数
        matcher.setHashIterations(1024);
        return matcher;
​
​
    }
​
​
​
​
    //配置Shiro过滤器链
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinition("/register.html","anon");
        chainDefinition.addPathDefinition("/error/500.html","anon");
        chainDefinition.addPathDefinition("/error","anon");
        chainDefinition.addPathDefinition("/user/register","anon");
        chainDefinition.addPathDefinition("/user/login","anon");
        chainDefinition.addPathDefinition("/user/**","authc");
        chainDefinition.addPathDefinition("/**","authc");
​
​
        return chainDefinition;
​
    }
​
    public List<String> getPermissionFromDB(TUser user) {
        if (user.getPerms()!=null){
            return user.getPerms();
        }
​
​
        Integer id = user.getId();
        //通过用户ID找到角色
​
        QueryWrapper<TUserRole> wrapper1 = new QueryWrapper<>();
        wrapper1.eq("userid",id);
        TUserRole tUserRole = tUserRoleMapper.selectOne(wrapper1);
        Integer roleid = tUserRole.getRoleid();
        System.out.println(roleid);
​
​
        QueryWrapper<TRolePerms> wrapper = new QueryWrapper<>();
        wrapper.select("permsid").eq("roleid",roleid);
        List<Object> permsidList = tRolePermsMapper.selectObjs(wrapper);
        System.out.println(permsidList);
        List<Integer> integers = permsidList.stream().map(o -> {
                    return (Integer) o;
                })
                .collect(Collectors.toList());
        List<TPers> tPers = tPersMapper.selectBatchIds(integers);
        System.out.println(tPers);
        ArrayList<String> strings = new ArrayList<>();
        for (TPers tPer : tPers) {
            strings.add(tPer.getName());
        }
        user.setPerms(strings);
​
        return strings;
    }
​
    public List<String> getRolesFromDB(TUser user) {
​
        if (user.getRoleName()!=null){
            return user.getRoleName();
        }
​
        Integer id = user.getId();
        //通过用户ID找到角色
        QueryWrapper<TUserRole> wrapper1 = new QueryWrapper<>();
        wrapper1.eq("userid",id);
        TUserRole tUserRole = tUserRoleMapper.selectOne(wrapper1);
        Integer roleid = tUserRole.getRoleid();
        QueryWrapper<TRole> wrapper = new QueryWrapper<>();
        wrapper.select("name").eq("id",roleid);
        List<Object> roles = tRoleMapper.selectObjs(wrapper);
        List<String> strings = roles.stream().map(o -> {
                    return (String) o;
                })
                .collect(Collectors.toList());
        user.setRoleName(strings);
        return strings;
    }
​
}

实体类

java 复制代码
package com.qf.shiroHomework.entity;
​
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.List;
​
import lombok.*;
​
/**
 * <p>
 * 
 * </p>
 *
 * @author jmj
 * @since 2023-08-10
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
@TableName("t_user")
public class TUser implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
​
    private String username;
​
    private String password;
​
    private String salt;
​
    @TableField(exist = false) // 说明当前这个属性在数据库表中没有对应的字段,让mp生成sql时忽略这个属性
    private List<String> roleName;
    @TableField(exist = false)
    private List<String> perms;
​
​
​
​
}

Controller

java 复制代码
//复杂密码匹配器
    @PostMapping("/login")
    public String login(TUser user){
        //使用Shiro进行登录处理
        Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
        //为了调用shiro的登录方法,需要准备一个Token对象
​
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
​
        subject.login(usernamePasswordToken);
​
        return "redirect:/show.html";
​
    }

以前手写MD5处理的方法

java 复制代码
//    简单密码匹配器方案
//    @PostMapping("/login")
//    public String login(TUser user){
//        //使用Shiro进行登录处理
//        Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
//        //为了调用shiro的登录方法,需要准备一个Token对象
//        //添加Where 条件
//        QueryWrapper<TUser> wrapper = new QueryWrapper<>();
//        wrapper.eq("username",user.getUsername());
//        TUser u = itUserService.getOne(wrapper);
//        if (u==null){
//            return "redirect:/index.html";
//        }else {
//
//            //MD5加密
//            Md5Hash md5Hash = new Md5Hash(user.getPassword(), u.getSalt(), 1024);
//            String newPassword = md5Hash.toHex();
//
//
//            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), newPassword);
//
//            subject.login(usernamePasswordToken);
//        }
//
//            return "redirect:/show.html";
//
//    }
相关推荐
骄马之死2 小时前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
GoGeekBaird3 小时前
Anthropic技能"(Skills)的经验分享
后端
王码码20353 小时前
多台服务器怎么统一看状态?Beszel 轻量监控,搭起来不费事
运维·服务器·后端·安全·阿里云·接口·web
郑洁文3 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
螺丝钉code4 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
指令集梦境5 小时前
Cursor + Spring Boot实战:从零写一个RESTful API
spring boot·后端·restful
摇滚侠5 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea
VidDown5 小时前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
码云之上6 小时前
聊聊如何设计一个高效、稳定的 Node.js 接入层
前端·后端·node.js
折哥的程序人生 · 物流技术专研6 小时前
Java 23 种设计模式:从踩坑到精通 | 原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?
java·设计模式·架构·原型模式·单一职责原则