• spring boot 整合 shiro 框架


    1、整合shiro

    1.1、创建spring boot项目
    1.2、引入依赖
    <dependency>
        <groupId>org.apache.shirogroupId>
        <artifactId>shiro-spring-boot-web-starterartifactId>
        <version>1.9.0version>
    dependency>
    
    
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.0.5version>
    dependency>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.46version>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    1.3、添加配置文件
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      mapper-locations: classpath:mapper/*.xml
    spring:
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/shirodb?characterEncoding=utf-8&useSSL=false
        username: root
        password: zzybzb
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    shiro:
      loginUrl: /myController/login
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1.4、启动类添加扫描包注解

    在这里插入图片描述

    2、登录认证实现

    2.1、库表
    2.1.1、建表
    CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
    USE `shirodb`;
    CREATE TABLE `user` (
     `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
     `name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
     `pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
     `rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
     PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.1.2、密码加密,添加几条数据
    public class ShiroMD5 {
        public static void main(String[] args) {
            String password = "123456";
    
            //为了保证安全,避免被破解还可以多次迭代加密,保证数据安全
            Md5Hash md53 = new Md5Hash(password,"salt",3);
            System.out.println("md5带盐的3次加密:"+md53.toHex());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    2.2、创建实体类
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer id;
        private String name;
        private String pwd;
        private Integer rid;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.3、创建 mapper
    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    }
    
    • 1
    • 2
    • 3
    2.4、创建 service
    public interface UserService {
        //用户登录
        User getUserInfoByName(String name);
    }
    
    • 1
    • 2
    • 3
    • 4
    @Service
    public class UserServiceImpl implements UserService {
    
        private UserMapper userMapper;
    
        @Autowired
        public UserServiceImpl(UserMapper userMapper) {
            this.userMapper = userMapper;
        }
    
        //根据姓名查询
        @Override
        public User getUserInfoByName(String name) {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("name",name);
            User user = userMapper.selectOne(wrapper);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.5、自定义realm
    @Component
    public class MyRealm extends AuthorizingRealm {
    
        private UserService userService;
    
        @Autowired
        public MyRealm(UserService userService){
            this.userService = userService;
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    
        //自定义登录认证方法
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //1获取用户身份信息
            String username = authenticationToken.getPrincipal().toString();
            //2调用业务层获取用户信息(数据库)
            User user = userService.getUserInfoByName(username);
            //3非空判断,将数据封装返回
            if (user != null){
                AuthenticationInfo info = new SimpleAuthenticationInfo(
                        authenticationToken.getPrincipal(),
                        user.getPwd(),
                        ByteSource.Util.bytes("salt"),
                        authenticationToken.getPrincipal().toString()
                );
                return info;
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    2.6、配置类
    @Configuration
    public class ShiroConfig {
        private MyRealm myRealm;
    
        @Autowired
        public ShiroConfig(MyRealm myRealm){
            this.myRealm = myRealm;
        }
    
        //配置SecurityManager
        @Bean
        public DefaultWebSecurityManager defaultWebSecurityManager(){
            //1创建defaultWebSecurityManager 对象
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            //2创建加密对象,设置相关属性
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
            //2.1采用md5加密
            matcher.setHashAlgorithmName("MD5");
            //2.2迭代加密次数
            matcher.setHashIterations(3);
            //3将加密对象存储到myRealm中
            myRealm.setCredentialsMatcher(matcher);
            //4将myRealm存入defaultWebSecurityManager 对象
            defaultWebSecurityManager.setRealm(myRealm);
    
            return defaultWebSecurityManager;
        }
    
        //配置Shiro内置过滤器拦截范围
        @Bean
        public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
            DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
            //设置不认证可以访问的资源
            definition.addPathDefinition("/my/userLogin","anon");
            definition.addPathDefinition("/my/login","anon");
            //设置登出过滤器
            definition.addPathDefinition("/logout","logout");
            //设置需要进行登录认证的拦截范围
            definition.addPathDefinition("/**","authc");
            //添加存在用户的过滤器(rememberMe)
            definition.addPathDefinition("/**","user");
            return  definition;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    2.7、controller
    @Controller
    @RequestMapping("/my")
    public class MyController {
    
        @RequestMapping("/userLogin")
        @ResponseBody
        public String login(String username,String password){
            //1获取subject对象
            Subject subject = SecurityUtils.getSubject();
            //2封装请求数据到token
            AuthenticationToken token = new UsernamePasswordToken(username,password);
            //3调用login方法进行登录认证
            try {
                subject.login(token);
                return "登录成功";
            } catch (AuthenticationException e) {
                e.printStackTrace();
                return "登录失败";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.8、访问,测试

    在这里插入图片描述在这里插入图片描述

    2.9、实现前端页面
    2.9.1、Thymeleaf依赖
    dependency>
       <groupId>org.springframework.bootgroupId>
       <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    2.9.2、login 页面
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>Shiro登录认证h1>
    <br>
    <form action="/my/userLogin">
        <div>用户名:<input type="text" name="username" value="">div>
        <div>密码:<input type="password" name="password" value="">div>
        <div><input type="submit" value="登录">div>
    form>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.9.3、添加 index 页面
    DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    
    <h1>Shiro登录认证后主页面h1>
    <br>
    登录用户为:<span th:text="${session.user}">span>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.9.4、添加 controller 方法,改造认证方法
    	//跳转登录页面
        @RequestMapping("/login")
        public String login(){
            return "login";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    2.9.5、修改配置文件

    在这里插入图片描述

    2.9.6、修改配置类

    在这里插入图片描述

    2.9.7、访问,测试

    在这里插入图片描述
    在这里插入图片描述

    3、多个 realm 的认证策略设置

    3.1、多个realm实现原理

    当应用程序配置多个 Realm 时,例如:用户名密码校验、手机号验证码校验等等。
    Shiro 的 ModularRealmAuthenticator 会使用内部的 AuthenticationStrategy 组件判断认证是成功还是失败。
    AuthenticationStrategy 是一个无状态的组件,它在身份验证尝试中被询问 4 次(这4 次交互所需的任何必要的状态将被作为方法参数):

    1. 在所有 Realm 被调用之前
    2. 在调用 Realm 的 getAuthenticationInfo 方法之前
    3. 在调用 Realm 的 getAuthenticationInfo 方法之后
    4. 在所有 Realm 被调用之后

    认证策略的另外一项工作就是聚合所有 Realm 的结果信息封装至一个
    AuthenticationInfo 实例中,并将此信息返回,以此作为 Subject 的身份信息。

    Shiro 中定义了 3 种认证策略的实现:

    • AtLeastOneSuccessfulStrategy
      只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功。

    • FirstSuccessfulStrategy
      第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略。

    • AllSuccessfulStrategy
      所有 Realm 成功,认证才视为成功。

    ModularRealmAuthenticator 内置的认证策略默认实现是
    AtLeastOneSuccessfulStrategy 方式。可以通过配置修改策略。

    3.2、多个realm代码实现
    //配置 SecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
     //1 创建 defaultWebSecurityManager 对象
     DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
     //2 创建认证对象,并设置认证策略
     ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
     modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
     
    defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
     //3 封装 myRealm 集合
     List<Realm> list = new ArrayList<>();
     list.add(myRealm);
     list.add(myRealm2);
     
     //4 将 myRealm 存入 defaultWebSecurityManager 对象
     defaultWebSecurityManager.setRealms(list);
     //5 返回
     return defaultWebSecurityManager;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4、remember me 功能

    Shiro 提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问。

    4.1、基本流程
    1. 首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会 把 RememberMe 的 Cookie 写到客户端并保存下来。
    2. 关闭浏览器再重新打开;会发现浏览器还是记住你的。
    3. 访问一般的网页服务器端,仍然知道你是谁,且能正常访问。
    4. 但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
    4.2、代码实现
    4.2.1、修改配置类
     //cookie属性设置
        public SimpleCookie rememberMeCookie(){
            SimpleCookie cookie = new SimpleCookie("rememberMe");
            //设置跨域
            //cookie.setDomain(domain);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(30*24*60*60);
            return cookie;
        }
        //创建Shiro的cookie管理对象
        public CookieRememberMeManager rememberMeManager(){
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
            cookieRememberMeManager.setCookie(rememberMeCookie());
            cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
            return cookieRememberMeManager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
    
    • 1

    在这里插入图片描述
    在这里插入图片描述

    4.2.2、添加,修改 controller

    添加

    	//登录认证验证 rememberMe
        @GetMapping("userLoginRm")
        public String userLogin(HttpSession session) {
            session.setAttribute("user","rememberMe");
            return "index";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    修改
    在这里插入图片描述

    4.2.3、修改登录页面
    <div>记住用户:<input type="checkbox" name="rememberMe" value="true">div>
    
    • 1

    在这里插入图片描述

    4.3、访问测试
    4.3.1、未勾选
    • 登录
      -
-

    在这里插入图片描述

    • 访问userLoginRm
      在这里插入图片描述
    • 关掉浏览器后打开,重新访问
      在这里插入图片描述
    4.3.2、勾选“记住我”
    • 登录

    在这里插入图片描述
    在这里插入图片描述

    • 关掉浏览器再访问
      在这里插入图片描述

    5、退出

    用户登录后,配套的有登出操作。直接通过Shiro过滤器即可实现登出。

    5.1、index页面
    <a href="/logout">登出a>
    
    • 1
    5.2、修改配置类
    //设置登出过滤器
    definition.addPathDefinition("/logout","logout");
    
    • 1
    • 2

    在这里插入图片描述

    5.3、测试

    在这里插入图片描述在这里插入图片描述

    6、授权、角色认证

    6.1、授权

    用户登录后,需要验证是否具有指定角色指定权限。Shiro也提供了方便的工具进行判断。这个工具就是Realm的doGetAuthorizationInfo方法进行判断。

    触发权限判断的有两种方式

    1. 在页面中通过shiro:****属性判断
    2. 在接口服务中通过注解@Requires****进行判断
    6.2、后端接口服务注解

    通过给接口服务方法添加注解可以实现权限校验,可以加在控制器方法上,也可以加在业务方法上,一般加在控制器方法上。常用注解如下:

    • @RequiresAuthentication
      验证用户是否登录,等同于方法subject.isAuthenticated()

    • @RequiresUser
      验证用户是否被记忆:
      登录认证成功subject.isAuthenticated()为true。
      登录后被记忆subject.isRemembered()为true。

    • @RequiresGuest
      验证是否是一个guest的请求,是否是游客的请求。
      此时subject.getPrincipal()为null。

    • @RequiresRoles

      验证subject是否有相应角色,有角色访问方法,没有则会抛出异常AuthorizationException。
      例如:

    @RequiresRoles(“aRoleName”)//只有subject有aRoleName角色才能访问方法someMethod()
    void someMethod();
    
    • 1
    • 2
    • @RequiresPermissions
      验证subject是否有相应权限,有权限访问方法,没有则会抛出异常AuthorizationException。
      例如:
    @RequiresPermissions ("file:read","wite:aFile.txt")//subject必须同时含有file:read和wite:aFile.txt权限才能访问方法someMethod()
    void someMethod();
    
    • 1
    • 2
    6.3、授权验证-没有角色无法访问
    6.3.1、controller代码
    	//登录认证验证角色
        @RequiresRoles("admin")
        @GetMapping("userLoginRoles")
        @ResponseBody
        public String userLoginRoles(){
            System.out.println("登录认证验证角色");
            return "验证角色成功";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    6.3.2、index页面
    <a shiro:hasRole="admin" href="/my/userLoginRoles">测试授权-角色验证a>
    
    • 1
    6.3.3、修改MyRealm类的doGetAuthorizationInfo方法

    在这里插入图片描述

    6.3.4、测试,没有admin角色,会报错

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    6.4、授权验证-获取角色进行验证
    6.4.1、修改 MyRealm 方法

    自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比

    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //自定义添加角色
    info.addRole("admin");
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    在这里插入图片描述在这里插入图片描述

    6.4.2、角色表,用户-角色中间表
    CREATE TABLE `role` (
     `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
     `name` VARCHAR(30) DEFAULT NULL COMMENT '角色名',
     `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
     `realname` VARCHAR(20) DEFAULT NULL COMMENT '角色显示名',
     PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色表';
    
    
    CREATE TABLE `role_user` (
     `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
     `uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
     `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
     PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射
    表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    自定义数据–角色表
    在这里插入图片描述

    自定义数据–用户角色中间表
    在这里插入图片描述

    6.4.3、查询 sql

    根据用户名查询对应角色信息

    SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE 
    uid=(SELECT id FROM USER WHERE NAME='zhangsan'));
    
    • 1
    • 2

    在这里插入图片描述

    6.4.4、mapper 方法
    @Select("SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE uid=(SELECT id FROM USER WHERE NAME=#{principal}))")
    List<String> getUserRoleInfoMapper(@Param("principal")String principal );
    
    • 1
    • 2
    6.4.5、service 方法
    /**
    * 根据用户查询角色
    * @param principal
    * @return
    */
    @Override
    public List<String> getUserRoleInfo(String principal) {
       return userMapper.getUserRoleInfoMapper(principal);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    6.4.6、MyRealm 方法改造
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("进入自定义授权方法");
        //1获取用户身份信息
        String principal = principalCollection.getPrimaryPrincipal().toString();
        //2调用业务层获取用户的角色信息(数据库)
        List<String> roles = userService.getUserRoleInfo(principal);
        System.out.println("当前用户角色信息 = " + roles);
        //2.5调用业务层获取用户的权限信息(数据库)
        //3创建对象,封装当前登录用户的角色、权限信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //自定义添加角色
        info.addRoles(roles);
        //返回信息
        return info;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    6.4.7、启动测试

    在这里插入图片描述在这里插入图片描述

    6.5、授权验证-获取权限进行验证
    6.5.1、权限表(菜单表),角色权限中间表
    CREATE TABLE `permissions` (
     `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
     `name` VARCHAR(30) DEFAULT NULL COMMENT '权限名',
     `info` VARCHAR(30) DEFAULT NULL COMMENT '权限信息',
     `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
     PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='权限表';
    
    
    CREATE TABLE `role_ps` (
     `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
     `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
     `pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
     PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    自定义数据–权限表
    在这里插入图片描述

    自定义数据–角色权限表

    在这里插入图片描述

    6.5.2、查询 sql

    根据角色名查询对应权限信息

    SELECT info FROM permissions WHERE id IN (SELECT pid FROM role_ps WHERE rid 
    IN (SELECT id FROM role WHERE NAME IN('admin','user')));
    
    • 1
    • 2
    6.5.3、mapper 方法
        @Select({
                ""
        })
        List<String> getUserPermissionInfoMapper(@Param("roles")List<String> roles);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    6.5.4、service代码
    /**
         * 根据角色查看权限
         * @param roles
         * @return
         */
        @Override
        public List<String> getUserPermissionInfo(List<String> roles) {
            return userMapper.getUserPermissionInfoMapper(roles);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    6.5.5、realm代码

    在这里插入图片描述

    6.5.6、controller 方法
    	//登录认证验证权限
        @RequiresPermissions("user:del")
        @GetMapping("userPermissions")
        @ResponseBody
        public String userLoginPermissions(){
            System.out.println("登录认证验证权限");
            return "验证权限成功";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    6.5.7、index页面
    <a shiro:hasPermission="user:delete" href="/my/userPermissions">测试授权-权限验证a>
    
    • 1
    6.5.8、测试

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    6.6、授权验证-异常处理

    创建认证异常处理类,使用@ControllerAdvice 加@ExceptionHandler 实现特殊异常处理。

    package com.bz.shiro.controller;
    
    import org.apache.shiro.authz.AuthorizationException;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @ControllerAdvice
    public class PermissionsException {
        @ResponseBody
        @ExceptionHandler(UnauthorizedException.class)
        public String unauthorizedException(Exception e){
            return "无权限";
        }
    
        @ResponseBody
        @ExceptionHandler(AuthorizationException.class)
        public String authorizationException(Exception e){
            return "权限认证失败";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    lisi账号登录
    在这里插入图片描述在这里插入图片描述

    6.7、前页面授权验证
    6.7.1、引入依赖
    
    <dependency>
        <groupId>com.github.theborakompanionigroupId>
        <artifactId>thymeleaf-extras-shiroartifactId>
        <version>2.0.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    6.7.2、配置类添加新配置
    	@Bean
        public ShiroDialect shiroDialect(){
            return new ShiroDialect();
        }
    
    • 1
    • 2
    • 3
    • 4
    6.7.3、Thymeleaf 中常用的 shiro:属性
    • guest 标签
      用户没有身份验证时显示相应信息,即游客访问信息。
    <shiro:guest>shiro:guest>
    
    • 1
    • user 标签
      用户已经身份验证/记住我登录后显示相应的信息。
    <shiro:user>shiro:user>
    
    • 1
    • authenticated 标签
      用户已经身份验证通过,即 Subject.login 登录成功,不是记住我登录的。
    <shiro:authenticated>shiro:authenticated>
    
    • 1
    • notAuthenticated 标签
      用户已经身份验证通过,即没有调用 Subject.login 进行登录,包括记住我自动登录的也属于未进行身份验证。
    <shiro:notAuthenticated>shiro:notAuthenticated>
    
    • 1
    • principal 标签
      相当于((User)Subject.getPrincipals()).getUsername()。
    <shiro: principal/><shiro:principal property="username"/>
    
    • 1
    • lacksPermission 标签
      如果当前 Subject 没有权限将显示 body 体内容。
    <shiro:lacksPermission name="org:create">shiro:lacksPermission>
    
    • 1
    • hasRole 标签
      如果当前 Subject 有角色将显示 body 体内容。
    <shiro:hasRole name="admin">shiro:hasRole>
    
    • 1
    • hasAnyRoles 标签
      如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。
    <shiro:hasAnyRoles name="admin,user">shiro:hasAnyRoles>
    
    • 1
    • lacksRole 标签
      如果当前 Subject 没有角色将显示 body 体内容。
    <shiro:lacksRole name="abc">shiro:lacksRole>
    
    • 1
    • hasPermission 标签
      如果当前 Subject 有权限将显示 body 体内容
    <shiro:hasPermission name="user:create">shiro:hasPermission>
    
    • 1
    6.7.4、修改index页面

    在这里插入图片描述在这里插入图片描述

    • zhangsan登录

    在这里插入图片描述
    在这里插入图片描述

    • lisi登录
      在这里插入图片描述在这里插入图片描述

    7、实现缓存

    7.1、缓存工具EhCache

    EhCache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。可以和大部分Java项目无缝整合,例如:Hibernate中的缓存就是基于EhCache实现的。

    EhCache支持内存和磁盘存储,默认存储在内存中,如内存不够时把缓存数据同步到磁盘中。EhCache支持基于Filter的Cache实现,也支持Gzip压缩算法。

    EhCache直接在JVM虚拟机中缓存,速度快,效率高;
    EhCache缺点是缓存共享麻烦,集群分布式应用使用不方便。

    7.2、新建项目
    7.2.1、引入依赖
    <dependency>
        <groupId>net.sf.ehcachegroupId>
        <artifactId>ehcacheartifactId>
        <version>2.6.11version>
        <type>pomtype>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    7.2.2、添加配置文件 ehcache.xml
    
    <ehcache>
        
        <diskStore path="java.io.tmpdir/ehcache"/>
        
        <defaultCache
                maxEntriesLocalHeap="10000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                maxEntriesLocalDisk="10000000"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
            <persistence strategy="localTempSwap"/>
        defaultCache>
    
        
        <cache name="HelloWorldCache"
               maxElementsInMemory="1000"
               eternal="false"
               timeToIdleSeconds="5"
               timeToLiveSeconds="5"
               overflowToDisk="false"
               memoryStoreEvictionPolicy="LRU"/>
    
        
        
    
    ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    7.2.3、测试类
    import net.sf.ehcache.Cache;
    import net.sf.ehcache.CacheManager;
    import net.sf.ehcache.Element;
    
    import java.io.InputStream;
    
    public class TestEH {
        public static void main(String[] args) {
            //获取编译目录下的资源的流对象
            InputStream input =
                    TestEH.class.getClassLoader().getResourceAsStream("ehcache.xml");
            //获取EhCache的缓存管理对象
            CacheManager cacheManager = new CacheManager(input);
            //获取缓存对象
            Cache cache = cacheManager.getCache("HelloWorldCache");
            //创建缓存数据
            Element element = new Element("name","zhang3");
            //存入缓存
            cache.put(element);
            //从缓存中取出数据输出
            Element element1 = cache.get("name");
            System.out.println("缓存中数据 = " + element1.getObjectValue());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    7.2.4、启动

    在这里插入图片描述

    7.3、Shiro整合EhCache

    Shiro官方提供了shiro-ehcache,实现了整合EhCache作为Shiro的缓存工具。可以缓存认证执行的Realm方法,减少对数据库的访问,提高认证效率。

    7.3.1、引入依赖
     
    <dependency>
        <groupId>org.apache.shirogroupId>
        <artifactId>shiro-ehcacheartifactId>
        <version>1.4.2version>
    dependency>
    <dependency>
        <groupId>commons-iogroupId>
        <artifactId>commons-ioartifactId>
        <version>2.6version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    7.3.2、在 resources 下添加配置文件 ehcache/ehcache-shiro.xml
    
    <ehcache name="ehcache" updateCheck="false">
        
        <diskStore path="java.io.tmpdir"/>
        
        <defaultCache
                maxEntriesLocalHeap="1000"
                eternal="false"
                timeToIdleSeconds="3600"
                timeToLiveSeconds="3600"
                overflowToDisk="false">
        defaultCache>
    
        
        <cache name="loginRolePsCache"
               maxEntriesLocalHeap="2000"
               eternal="false"
               timeToIdleSeconds="600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true"/>
    
    ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    7.3.3、修改配置类 ShiroConfig
    	//缓存管理器
        public EhCacheManager getEhCacheManager(){
            EhCacheManager ehCacheManager = new EhCacheManager();
            InputStream is =null;
            try {
                is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
            } catch (IOException e) {
                e.printStackTrace();
            }
            CacheManager cacheManager = new CacheManager(is);
            ehCacheManager.setCacheManager(cacheManager);
            return ehCacheManager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述在这里插入图片描述

    7.3.4、测试
    • 第一次登录可以看到查询角色、权限信息
      在这里插入图片描述

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    • 先清除日志,再点击角色认证、权限认证,查看日志,没有查询数据库

      在这里插入图片描述 在这里插入图片描述

    8、会话管理

    8.1、SessionManager

    会话管理器,负责创建和管理用户的会话(Session)生命周期,它能够在任何环境中在本地管理用户会话,即使没有Web/Servlet/EJB容器,也一样可以保存会话。默认情况下,Shiro会检测当前环境中现有的会话机制(比如Servlet容器)进行适配,如果没有(比如独立应用程序或者非Web环境),它将会使用内置的企业会话管理器来提供相应的会话管理服务,其中还涉及一个名为SessionDAO的对象。SessionDAO负责Session的持久化操作(CRUD),允许Session数据写入到后端持久化数据库。

    8.2、会话管理实现

    SessionManager由SecurityManager管理。
    Shiro提供了三种实现
    在这里插入图片描述

    1. DefaultSessionManager:用于JavaSE环境
    2. ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
    3. DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的 会话管理)。
    8.3、获得session方式
    1. 实现
    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute(“key”,”value”)
    
    • 1
    • 2
    1. 说明

      Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成ShiroHttpServletRequest。

      SecurityManager 和 SessionManager 会话管理器决定 session 来源于 ServletRequest还是由 Shiro 管理的会话。

      无论是通过 request.getSession 或 subject.getSession 获取到 session,操作session,两者都是等价的。

    结束!!!

    							人应尊敬他自己,并应自视能配得上最高尚的东西。——黑格尔
    
    • 1
  • 相关阅读:
    SOCKS5代理、代理IP、HTTP与网络安全
    【最佳实践】MongoDB导出导入数据
    【Git笔记】之Git重命名详解
    (计算机组成原理)第五章中央处理器-第六节3:五段式指令流水线
    Linux下c++串口编程
    Vim编辑器
    postman定义公共函数这样写,测试组长直呼牛逼!!!
    机器学习算法一之DBSCAN聚类原理与实现(二维及三维)
    4.1.9-映射应用程序体系结构
    Orleans的成员管理和故障检测故障检测
  • 原文地址:https://blog.csdn.net/weixin_49107940/article/details/127652818