SpringBoot整合Shiro

哈喽,大家好,我是花臂,今天给大家讲一下如何使用springboot整合shiro的实战。
shiro三个核心API:

  1. Subject : 描述用户对象
  2. SecurityManager : 安全管理器对象,封装shiro功能的对象
  3. Realm : 安全数据的提供者

建表

//用户表
create table t_user(
id int primary key  auto_increment,
username varchar(20) not null  unique,
password varchar(100) not null
)engine=innodb default charset=utf8

//角色表
create table t_role(
id int primary key auto_increment,
role_name varchar(50) not null unique,
create_time timestamp not null
)engine=innodb default charset=utf8

//权限表
create table t_permission(
id int primary key auto_increment,
permission_name varchar(50) not null unique,
create_time timestamp
)engine=innodb default charset=utf8

//用户角色中间表
create table t_user_role(
id int primary key auto_increment,
user_id int references t_user(id),
role_id int references t_role(id),
unique(user_id,role_id)
)engine=innodb default charset=utf8

//角色权限中间表
create table t_role_permission(
id int primary key auto_increment,
permission_id int references t_permission(id),
role_id int references t_role(id),
unique(permission_id,role_id)
)engine=innodb default charset=utf8

pom文件

 <!-- springboot父工程 -->     
     <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
    </parent>
    <dependencies>
      <!-- web启动器-->    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
       <!-- shiro依赖-->  
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
         <!-- mysql数据库驱动--> 
         <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
            <scope>runtime</scope>
        </dependency>
          <!-- mybatis启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- springboot整合jdbc连接池 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.0.7.RELEASE</version>
        </dependency>
    </dependencies>

核心代码

自定义ShiroConfig配置类

@Configuration
public class ShiroConfig {
    /**
     * 创建安全管理器
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        return securityManager;
    }

    /**
     * 创建realm
     */
    @Bean
    public MyRealm realm() {
        MyRealm myRealm = new MyRealm();
        //密码比对器
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myRealm;
    }

    /**
     * 创建密码比对器,将登录进来的密码以如下的方法进行加密然后跟数据的进行比对
     */

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("SHA-256");
        hashedCredentialsMatcher.setHashIterations(10000);
        //默认是true,此时密码加密会使用Hex编码(tostring()),false时使用base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
        return hashedCredentialsMatcher;
    }

    /**
     * 开启shiro注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorization = new AuthorizationAttributeSourceAdvisor();
        authorization.setSecurityManager(securityManager());
        return authorization;
    }

    /**
     * 开启aop注解支持(主要用于shiro注解上面)
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }

    /***
     * 配置shiro的过滤器工厂
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        //设置过滤器工厂
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        //通用配置(未登录跳转页面,未授权跳转页面)
        shiroFilterFactoryBean.setLoginUrl("/user/tologin");
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthor");
        //设置过滤器集合
        /**
         *   设置所有的过滤器,有顺序map
         * key=拦截的url地址
         * value=过滤器类型
         */
        Map map = new LinkedHashMap();
        map.put("/user/login", "anon");
        map.put("/user/register", "anon");
        map.put("/user/list", "authc");
        map.put("/user/testRole", "roles[teacher]");
        map.put("/**","authc");//所有请求都必须登录才能访问,写在下面,过滤器会从上到下依次执行
       shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
}

自定义realm类

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    //权限校验
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取当前用户名
        String username = (String) principals.getPrimaryPrincipal();
        User user = new User();
        user.setUsername(username);
        //存放角色名称
        Set<String> roleSet = new HashSet<>();
        //存放权限标识
        Set<String> persSet = new HashSet<>();
        //从数据库中查询出当前用的权限和角色信息
        List<User> users = userService.searchAllUser(user);
        for (User user1 : users) {
            Set<Role> roles = user1.getRoles();
            for (Role role : roles) {
                roleSet.add(role.getRoleName());
            }
            Set<Permission> permissions = user1.getPermissions();
            for (Permission permission : permissions) {
                persSet.add(permission.getPermissionName());
            }
        }
        //将用户的角色和权限信息封装在 AuthorizationInfo 子类中的SimpleAuthorizationInfo
        info.setRoles(roleSet);
        info.setStringPermissions(persSet);
        return info;
    }
    //身份认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = new User();
        user.setUsername(username);
        List<User> users = userService.searchAllUser(user);
        if (!users.isEmpty()) {
            return new SimpleAuthenticationInfo(
                    users.get(0).getUsername(), //数据库的用户名
                    users.get(0).getPassword(), //数据库的密码
                    ByteSource.Util.bytes(users.get(0).getSalt()), //用户密码的盐
                    this.getName());//realm标识(底层采用className+atomic递增)
        }
        return null;
    }
}

service层

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper  userMapper;
    @Override
    public List<User> searchAllUser(User user) {
        List<User> users = userMapper.searchAllUser(user);
        return users;
    }

    @Override
    public int insertAddUser(User user) {
        //设置随机盐
        String salt = UUID.randomUUID().toString();
        //设置加密属性,:sha256算法,加盐,迭代10000次
        String s = new Sha256Hash(user.getPassword(), salt, 10000).toBase64();
        user.setPassword(s);//密文采用base64格式化
        user.setSalt(salt);

        return userMapper.insertAddUser(user);
    }
}

mapper层

public interface UserMapper {
    List<User> searchAllUser(User user);
    int insertAddUser(User user);
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shiro.mapper.UserMapper">

    <resultMap id="resultUserMap" type="User">
        <result property="id" column="id"></result>
        <result property="username" column="username"></result>
        <result property="password" column="password"></result>
        <result property="salt" column="salt"></result>
        <collection property="roles"  ofType="Role">
            <id property="id" column="roleID" ></id>
            <result property="roleName" column="roleName"></result>
            <result property="createTime" column="role_createTime"></result>
        </collection>
        <collection property="permissions" ofType="Permission">
            <id property="id" column="permissionID" ></id>
            <result property="permissionName" column="permissionName"></result>
            <result property="createTime" column="per_createTime"></result>
        </collection>
    </resultMap>

<select id="searchAllUser"  parameterType="User"  resultMap="resultUserMap">
    SELECT
    t_user.id id,
    t_user.username username,
    t_user.`password` password,
    t_user.salt salt,
    t_role.id roleID,
    t_role.role_name roleName,
    t_role.create_time role_createTime,
    t_permission.id permissionID,
    t_permission.permission_name permissionName,
    t_permission.create_time per_createTime
    FROM
    t_user
    LEFT JOIN t_user_role ON t_user.id = t_user_role.user_id
    LEFT JOIN t_role ON t_role.id = t_user_role.role_id
    LEFT JOIN t_role_permission ON t_role_permission.role_id = t_role.id
    LEFT JOIN t_permission ON t_permission.id = t_role_permission.permission_id
<where>
    <if test="username!=null and username!=''">and t_user.username=#{username}</if>
</where>
;
</select>

    <insert id="insertAddUser" parameterType="User">
        insert into t_user(username,password,salt)VALUES(#{username},#{password},#{salt})

    </insert>
</mapper>

application.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.shiro.pojo

controller层

@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("list")
    @ResponseBody
    public List<User> searchAllUser() {
        User user = new User();
        List<User> users = userService.searchAllUser(user);
        return users;

    }

    //登录
    @RequestMapping(value = "login", method = RequestMethod.POST)
    @ResponseBody
    public String login(User user) {
        try {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
            subject.login(token);
        } catch (Exception e) {
            e.printStackTrace();
            return "登录失败!";
        }
        return "登录成功!";

    }

    @RequestMapping(value = "register", method = RequestMethod.POST)
    @ResponseBody
    public String register(User user) {
        int i = userService.insertAddUser(user);
        if (i > 0) {
            return "添加成功!";
        }

        return "添加失败";

    }

    //使用shiro注解鉴权(常用如下:)
    // @RequiresPermissions() --访问此方法必须具有的权限
    // @RequiresRoles()       --访问此方法必须具有的角色

    /***
     * 1.过滤器:如果权限信息不匹配会走/user/unauthor地址(shiroFactoryBean配置的)
     * 2.注解:如果权限信息不匹配会抛出异常
     * @return
     */

    @RequestMapping("testRole")
    @ResponseBody
    public String testRole() {
       return "测试角色成功!";
    }

    @RequiresPermissions("student:xx")
    @RequestMapping("testPerms")
    @ResponseBody
    public String testPerms() {
        return "测试权限成功!";
    }

    @RequestMapping("tologin")
    @ResponseBody
    public String tologin() {
        return "请先登录";
    }
    @RequestMapping("unauthor")
    @ResponseBody
    public String unauthor() {
        return "权限不足";
    }

}

异常处理类

@ControllerAdvice
public class RunException {
    //没有权限时候抛出异常会被捕获
    @ExceptionHandler(AuthorizationException.class) 
    @ResponseBody
    public String error(){
        return "没有权限访问,谢谢";
    }
}

OK,本次教程从项目实战角度演示了shiro的认证、授权、加密等功能,有什么疑问欢迎下方评论区留言!

评论区



© [2020] · Powered by Typecho · Theme by Morecho
鄂ICP备20005123号