当前位置: 移动技术网 > IT编程>开发语言>Java > SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录

SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录

2019年08月23日  | 移动技术网IT编程  | 我要评论

钱文忠老婆,魅力研习社全集,汉献帝新传下载

作者:sans_

juejin.im/post/5d087d605188256de9779e64

一.说明

shiro是一个安全框架,项目中主要用它做认证,授权,加密,以及用户的会话管理,虽然shiro没有springsecurity功能更丰富,但是它轻量,简单,在项目中通常业务需求shiro也都能胜任.

二.项目环境

  • mybatis-plus版本: 3.1.0

  • springboot版本:2.1.5

  • jdk版本:1.8

  • shiro版本:1.4

  • shiro-redis插件版本:3.1.0

数据表(sql文件在项目中):数据库中测试号的密码进行了加密,密码皆为123456

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

maven依赖如下:

<dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <dependency>
            <groupid>mysql</groupid>
            <artifactid>mysql-connector-java</artifactid>
            <scope>runtime</scope>
        </dependency>
        <!-- aop依赖,一定要加,否则权限拦截验证不生效 -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-aop</artifactid>
        </dependency>
        <!-- lombok插件 -->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <!-- redis -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-data-redis-reactive</artifactid>
        </dependency>
        <!-- mybatisplus 核心库 -->
        <dependency>
            <groupid>com.baomidou</groupid>
            <artifactid>mybatis-plus-boot-starter</artifactid>
            <version>3.1.0</version>
        </dependency>
        <!-- 引入阿里数据库连接池 -->
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>druid</artifactid>
            <version>1.1.6</version>
        </dependency>
        <!-- shiro 核心依赖 -->
        <dependency>
            <groupid>org.apache.shiro</groupid>
            <artifactid>shiro-spring</artifactid>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro-redis插件 -->
        <dependency>
            <groupid>org.crazycake</groupid>
            <artifactid>shiro-redis</artifactid>
            <version>3.1.0</version>
        </dependency>
        <!-- stringuitls工具 -->
        <dependency>
            <groupid>org.apache.commons</groupid>
            <artifactid>commons-lang3</artifactid>
            <version>3.5</version>
        </dependency>
</dependencies>

配置如下:

# 配置端口
server:
  port: 8764
spring:
  # 配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.driver
    url: jdbc:mysql://localhost:3306/my_shiro?servertimezone=utc&useunicode=true&characterencoding=utf-8&zerodatetimebehavior=converttonull&usessl=false
    username: root
    password: root
    type: com.alibaba.druid.pool.druiddatasource
  # redis数据源
  redis:
    host: localhost
    port: 6379
    timeout: 6000
    password: 123456
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接
# mybatis-plus相关配置
mybatis-plus:
  # xml扫描,多个目录用逗号或者分号分隔(告诉 mapper 所对应的 xml 文件位置)
  mapper-locations: classpath:mapper/*.xml
  # 以下配置均有默认值,可以不设置
  global-config:
    db-config:
      #主键类型 auto:"数据库id自增" input:"用户输入id",id_worker:"全局唯一id (数字类型唯一id)", uuid:"全局唯一id uuid";
      id-type: auto
      #字段策略 ignored:"忽略判断"  not_null:"非 null 判断")  not_empty:"非空判断"
      field-strategy: not_empty
      #数据库类型
      db-type: mysql
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 mybatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.stdoutimpl

二.编写项目基础类

用户实体,dao,service等在这里省略,请参考源码

编写exception类来处理shiro权限拦截异常

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

 

创建sha256util加密工具

 

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

创建spring工具

/**
 * @description spring上下文工具类
 * @author sans
 * @createtime 2019/6/17 13:40
 */
@component
public class springutil implements applicationcontextaware {
    private static applicationcontext context;
    /**
     * spring在bean初始化后会判断是不是applicationcontextaware的子类
     * 如果该类是,setapplicationcontext()方法,会将容器中applicationcontext作为参数传入进去
     * @author sans
     * @createtime 2019/6/17 16:58
     */
    @override
    public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception {
        context = applicationcontext;
    }
    /**
     * 通过name返回指定的bean
     * @author sans
     * @createtime 2019/6/17 16:03
     */
    public static <t> t getbean(class<t> beanclass) {
        return context.getbean(beanclass);
    }
}

创建shiro工具

/**
 * @description shiro工具类
 * @author sans
 * @createtime 2019/6/15 16:11
 */
public class shiroutils {

    /** 私有构造器 **/
    private shiroutils(){}

    private static redissessiondao redissessiondao = springutil.getbean(redissessiondao.class);

    /**
     * 获取当前用户session
     * @author sans
     * @createtime 2019/6/17 17:03
     * @return sysuserentity 用户信息
     */
    public static session getsession() {
        return securityutils.getsubject().getsession();
    }

    /**
     * 用户登出
     * @author sans
     * @createtime 2019/6/17 17:23
     */
    public static void logout() {
        securityutils.getsubject().logout();
    }

    /**
    * 获取当前用户信息
    * @author sans
    * @createtime 2019/6/17 17:03
    * @return sysuserentity 用户信息
    */
    public static sysuserentity getuserinfo() {
      return (sysuserentity) securityutils.getsubject().getprincipal();
    }

    /**
     * 删除用户缓存信息
     * @author sans
     * @createtime 2019/6/17 13:57
     * @param  username  用户名称
     * @param  isremovesession 是否删除session
     * @return void
     */
    public static void deletecache(string username, boolean isremovesession){
        //从缓存中获取session
        session session = null;
        collection<session> sessions = redissessiondao.getactivesessions();
        sysuserentity sysuserentity;
        object attribute = null;
        for(session sessioninfo : sessions){
            //遍历session,找到该用户名称对应的session
            attribute = sessioninfo.getattribute(defaultsubjectcontext.principals_session_key);
            if (attribute == null) {
                continue;
            }
            sysuserentity = (sysuserentity) ((simpleprincipalcollection) attribute).getprimaryprincipal();
            if (sysuserentity == null) {
                continue;
            }
            if (objects.equals(sysuserentity.getusername(), username)) {
                session=sessioninfo;
            }
        }
        if (session == null||attribute == null) {
            return;
        }
        //删除session
        if (isremovesession) {
            redissessiondao.delete(session);
        }
        //删除cache,在访问受限接口时会重新授权
        defaultwebsecuritymanager securitymanager = (defaultwebsecuritymanager) securityutils.getsecuritymanager();
        authenticator authc = securitymanager.getauthenticator();
        ((logoutaware) authc).onlogout((simpleprincipalcollection) attribute);
    }
}

创建shiro的sessionid生成器

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

三.编写shiro核心类

创建realm用于授权和认证

/**
 * @description shiro权限匹配和账号密码匹配
 * @author sans
 * @createtime 2019/6/15 11:27
 */
public class shirorealm extends authorizingrealm {
    @autowired
    private sysuserservice sysuserservice;
    @autowired
    private sysroleservice sysroleservice;
    @autowired
    private sysmenuservice sysmenuservice;
    /**
     * 授权权限
     * 用户进行权限验证时候shiro会去缓存中找,如果查不到数据,会执行这个方法去查权限,并放入缓存中
     * @author sans
     * @createtime 2019/6/12 11:44
     */
    @override
    protected authorizationinfo dogetauthorizationinfo(principalcollection principalcollection) {
        simpleauthorizationinfo authorizationinfo = new simpleauthorizationinfo();
        sysuserentity sysuserentity = (sysuserentity) principalcollection.getprimaryprincipal();
        //获取用户id
        long userid =sysuserentity.getuserid();
        //这里可以进行授权和处理
        set<string> rolesset = new hashset<>();
        set<string> permsset = new hashset<>();
        //查询角色和权限(这里根据业务自行查询)
        list<sysroleentity> sysroleentitylist = sysroleservice.selectsysrolebyuserid(userid);
        for (sysroleentity sysroleentity:sysroleentitylist) {
            rolesset.add(sysroleentity.getrolename());
            list<sysmenuentity> sysmenuentitylist = sysmenuservice.selectsysmenubyroleid(sysroleentity.getroleid());
            for (sysmenuentity sysmenuentity :sysmenuentitylist) {
                permsset.add(sysmenuentity.getperms());
            }
        }
        //将查到的权限和角色分别传入authorizationinfo中
        authorizationinfo.setstringpermissions(permsset);
        authorizationinfo.setroles(rolesset);
        return authorizationinfo;
    }

    /**
     * 身份认证
     * @author sans
     * @createtime 2019/6/12 12:36
     */
    @override
    protected authenticationinfo dogetauthenticationinfo(authenticationtoken authenticationtoken) throws authenticationexception {
        //获取用户的输入的账号.
        string username = (string) authenticationtoken.getprincipal();
        //通过username从数据库中查找 user对象,如果找到进行验证
        //实际项目中,这里可以根据实际情况做缓存,如果不做,shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        sysuserentity user = sysuserservice.selectuserbyname(username);
        //判断账号是否存在
        if (user == null) {
            throw new authenticationexception();
        }
        //判断账号是否被冻结
        if (user.getstate()==null||user.getstate().equals("prohibit")){
            throw new lockedaccountexception();
        }
        //进行验证
        simpleauthenticationinfo authenticationinfo = new simpleauthenticationinfo(
                user,                                  //用户名
                user.getpassword(),                    //密码
                bytesource.util.bytes(user.getsalt()), //设置盐值
                getname()
        );
        //验证成功开始踢人(清除缓存和session)
        shiroutils.deletecache(username,true);
        return authenticationinfo;
    }
}

创建sessionmanager类

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

创建shiroconfig配置类

/**
 * @description shiro配置类
 * @author sans
 * @createtime 2019/6/10 17:42
 */
@configuration
public class shiroconfig {

    private final string cache_key = "shiro:cache:";
    private final string session_key = "shiro:session:";
    private final int expire = 1800;

    //redis配置
    @value("${spring.redis.host}")
    private string host;
    @value("${spring.redis.port}")
    private int port;
    @value("${spring.redis.timeout}")
    private int timeout;
    @value("${spring.redis.password}")
    private string password;

    /**
     * 开启shiro-aop注解支持
     * @attention 使用代理方式所以需要开启代码支持
     * @author sans
     * @createtime 2019/6/12 8:38
     */
    @bean
    public authorizationattributesourceadvisor authorizationattributesourceadvisor(securitymanager securitymanager) {
        authorizationattributesourceadvisor authorizationattributesourceadvisor = new authorizationattributesourceadvisor();
        authorizationattributesourceadvisor.setsecuritymanager(securitymanager);
        return authorizationattributesourceadvisor;
    }

    /**
     * shiro基础配置
     * @author sans
     * @createtime 2019/6/12 8:42
     */
    @bean
    public shirofilterfactorybean shirofilterfactory(securitymanager securitymanager){
        shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();
        shirofilterfactorybean.setsecuritymanager(securitymanager);
        map<string, string> filterchaindefinitionmap = new linkedhashmap<>();
        // 注意过滤器配置顺序不能颠倒
        // 配置过滤:不会被拦截的链接
        filterchaindefinitionmap.put("/static/**", "anon");
        filterchaindefinitionmap.put("/userlogin/**", "anon");
        filterchaindefinitionmap.put("/**", "authc");
        // 配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        shirofilterfactorybean.setloginurl("/userlogin/unauth");
        shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap);
        return shirofilterfactorybean;
    }

    /**
     * 安全管理器
     * @author sans
     * @createtime 2019/6/12 10:34
     */
    @bean
    public securitymanager securitymanager() {
        defaultwebsecuritymanager securitymanager = new defaultwebsecuritymanager();
        // 自定义ssession管理
        securitymanager.setsessionmanager(sessionmanager());
        // 自定义cache实现
        securitymanager.setcachemanager(cachemanager());
        // 自定义realm验证
        securitymanager.setrealm(shirorealm());
        return securitymanager;
    }

    /**
     * 身份验证器
     * @author sans
     * @createtime 2019/6/12 10:37
     */
    @bean
    public shirorealm shirorealm() {
        shirorealm shirorealm = new shirorealm();
        shirorealm.setcredentialsmatcher(hashedcredentialsmatcher());
        return shirorealm;
    }

    /**
     * 凭证匹配器
     * 将密码校验交给shiro的simpleauthenticationinfo进行处理,在这里做匹配配置
     * @author sans
     * @createtime 2019/6/12 10:48
     */
    @bean
    public hashedcredentialsmatcher hashedcredentialsmatcher() {
        hashedcredentialsmatcher shacredentialsmatcher = new hashedcredentialsmatcher();
        // 散列算法:这里使用sha256算法;
        shacredentialsmatcher.sethashalgorithmname(sha256util.hash_algorithm_name);
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        shacredentialsmatcher.sethashiterations(sha256util.hash_iterations);
        return shacredentialsmatcher;
    }

    /**
     * 配置redis管理器
     * @attention 使用的是shiro-redis开源插件
     * @author sans
     * @createtime 2019/6/12 11:06
     */
    @bean
    public redismanager redismanager() {
        redismanager redismanager = new redismanager();
        redismanager.sethost(host);
        redismanager.setport(port);
        redismanager.settimeout(timeout);
        redismanager.setpassword(password);
        return redismanager;
    }

    /**
     * 配置cache管理器
     * 用于往redis存储权限和角色标识
     * @attention 使用的是shiro-redis开源插件
     * @author sans
     * @createtime 2019/6/12 12:37
     */
    @bean
    public rediscachemanager cachemanager() {
        rediscachemanager rediscachemanager = new rediscachemanager();
        rediscachemanager.setredismanager(redismanager());
        rediscachemanager.setkeyprefix(cache_key);
        // 配置缓存的话要求放在session里面的实体类必须有个id标识
        rediscachemanager.setprincipalidfieldname("userid");
        return rediscachemanager;
    }

    /**
     * sessionid生成器
     * @author sans
     * @createtime 2019/6/12 13:12
     */
    @bean
    public shirosessionidgenerator sessionidgenerator(){
        return new shirosessionidgenerator();
    }

    /**
     * 配置redissessiondao
     * @attention 使用的是shiro-redis开源插件
     * @author sans
     * @createtime 2019/6/12 13:44
     */
    @bean
    public redissessiondao redissessiondao() {
        redissessiondao redissessiondao = new redissessiondao();
        redissessiondao.setredismanager(redismanager());
        redissessiondao.setsessionidgenerator(sessionidgenerator());
        redissessiondao.setkeyprefix(session_key);
        redissessiondao.setexpire(expire);
        return redissessiondao;
    }

    /**
     * 配置session管理器
     * @author sans
     * @createtime 2019/6/12 14:25
     */
    @bean
    public sessionmanager sessionmanager() {
        shirosessionmanager shirosessionmanager = new shirosessionmanager();
        shirosessionmanager.setsessiondao(redissessiondao());
        return shirosessionmanager;
    }
}

四.实现权限控制

shiro可以用代码或者注解来控制权限,通常我们使用注解控制,不仅简单方便,而且更加灵活.shiro注解一共有五个:

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

一般情况下我们在项目中做权限控制,使用最多的是requirespermissions和requiresroles,允许存在多个角色和权限,默认逻辑是and,也就是同时拥有这些才可以访问方法,可以在注解中以参数的形式设置成or

示例

 

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

使用顺序:shiro注解是存在顺序的,当多个注解在一个方法上的时候,会逐个检查,知道全部通过为止,默认拦截顺序是:requiresroles->requirespermissions->requiresauthentication->
requiresuser->requiresguest

示例

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

创建userrolecontroller角色拦截测试类

/**
 * @description 角色测试
 * @author sans
 * @createtime 2019/6/19 11:38
 */
@restcontroller
@requestmapping("/role")
public class userrolecontroller {

    @autowired
    private sysuserservice sysuserservice;
    @autowired
    private sysroleservice sysroleservice;
    @autowired
    private sysmenuservice sysmenuservice;
    @autowired
    private sysrolemenuservice sysrolemenuservice;

    /**
     * 管理员角色测试接口
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getadmininfo")
    @requiresroles("admin")
    public map<string,object> getadmininfo(){
        map<string,object> map = new hashmap<>();
        map.put("code",200);
        map.put("msg","这里是只有管理员角色能访问的接口");
        return map;
    }

    /**
     * 用户角色测试接口
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getuserinfo")
    @requiresroles("user")
    public map<string,object> getuserinfo(){
        map<string,object> map = new hashmap<>();
        map.put("code",200);
        map.put("msg","这里是只有用户角色能访问的接口");
        return map;
    }

    /**
     * 角色测试接口
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getroleinfo")
    @requiresroles(value={"admin","user"},logical = logical.or)
    @requiresuser
    public map<string,object> getroleinfo(){
        map<string,object> map = new hashmap<>();
        map.put("code",200);
        map.put("msg","这里是只要有admin或者user角色能访问的接口");
        return map;
    }

    /**
     * 登出(测试登出)
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getlogout")
    @requiresuser
    public map<string,object> getlogout(){
        shiroutils.logout();
        map<string,object> map = new hashmap<>();
        map.put("code",200);
        map.put("msg","登出");
        return map;
    }
}

创建usermenucontroller权限拦截测试类

/**
 * @description 权限测试
 * @author sans
 * @createtime 2019/6/19 11:38
 */
@restcontroller
@requestmapping("/menu")
public class usermenucontroller {

    @autowired
    private sysuserservice sysuserservice;
    @autowired
    private sysroleservice sysroleservice;
    @autowired
    private sysmenuservice sysmenuservice;
    @autowired
    private sysrolemenuservice sysrolemenuservice;

    /**
     * 获取用户信息集合
     * @author sans
     * @createtime 2019/6/19 10:36
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getuserinfolist")
    @requirespermissions("sys:user:info")
    public map<string,object> getuserinfolist(){
        map<string,object> map = new hashmap<>();
        list<sysuserentity> sysuserentitylist = sysuserservice.list();
        map.put("sysuserentitylist",sysuserentitylist);
        return map;
    }

    /**
     * 获取角色信息集合
     * @author sans
     * @createtime 2019/6/19 10:37
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getroleinfolist")
    @requirespermissions("sys:role:info")
    public map<string,object> getroleinfolist(){
        map<string,object> map = new hashmap<>();
        list<sysroleentity> sysroleentitylist = sysroleservice.list();
        map.put("sysroleentitylist",sysroleentitylist);
        return map;
    }

    /**
     * 获取权限信息集合
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getmenuinfolist")
    @requirespermissions("sys:menu:info")
    public map<string,object> getmenuinfolist(){
        map<string,object> map = new hashmap<>();
        list<sysmenuentity> sysmenuentitylist = sysmenuservice.list();
        map.put("sysmenuentitylist",sysmenuentitylist);
        return map;
    }

    /**
     * 获取所有数据
     * @author sans
     * @createtime 2019/6/19 10:38
     * @return map<string,object> 返回结果
     */
    @requestmapping("/getinfoall")
    @requirespermissions("sys:info:all")
    public map<string,object> getinfoall(){
        map<string,object> map = new hashmap<>();
        list<sysuserentity> sysuserentitylist = sysuserservice.list();
        map.put("sysuserentitylist",sysuserentitylist);
        list<sysroleentity> sysroleentitylist = sysroleservice.list();
        map.put("sysroleentitylist",sysroleentitylist);
        list<sysmenuentity> sysmenuentitylist = sysmenuservice.list();
        map.put("sysmenuentitylist",sysmenuentitylist);
        return map;
    }

    /**
     * 添加管理员角色权限(测试动态权限更新)
     * @author sans
     * @createtime 2019/6/19 10:39
     * @param  username 用户id
     * @return map<string,object> 返回结果
     */
    @requestmapping("/addmenu")
    public map<string,object> addmenu(){
        //添加管理员角色权限
        sysrolemenuentity sysrolemenuentity = new sysrolemenuentity();
        sysrolemenuentity.setmenuid(4l);
        sysrolemenuentity.setroleid(1l);
        sysrolemenuservice.save(sysrolemenuentity);
        //清除缓存
        string username = "admin";
        shiroutils.deletecache(username,false);
        map<string,object> map = new hashmap<>();
        map.put("code",200);
        map.put("msg","权限添加成功");
        return map;
    }
}

创建userlogincontroller登录类

/**
 * @description 用户登录
 * @author sans
 * @createtime 2019/6/17 15:21
 */
@restcontroller
@requestmapping("/userlogin")
public class userlogincontroller {

    @autowired
    private sysuserservice sysuserservice;

    /**
     * 登录
     * @author sans
     * @createtime 2019/6/20 9:21
     */
    @requestmapping("/login")
    public map<string,object> login(@requestbody sysuserentity sysuserentity){
        map<string,object> map = new hashmap<>();
        //进行身份验证
        try{
            //验证身份和登陆
            subject subject = securityutils.getsubject();
            usernamepasswordtoken token = new usernamepasswordtoken(sysuserentity.getusername(), sysuserentity.getpassword());
            //验证成功进行登录操作
            subject.login(token);
        }catch (incorrectcredentialsexception e) {
            map.put("code",500);
            map.put("msg","用户不存在或者密码错误");
            return map;
        } catch (lockedaccountexception e) {
            map.put("code",500);
            map.put("msg","登录失败,该用户已被冻结");
            return map;
        } catch (authenticationexception e) {
            map.put("code",500);
            map.put("msg","该用户不存在");
            return map;
        } catch (exception e) {
            map.put("code",500);
            map.put("msg","未知异常");
            return map;
        }
        map.put("code",0);
        map.put("msg","登录成功");
        map.put("token",shiroutils.getsession().getid().tostring());
        return map;
    }
    /**
     * 未登录
     * @author sans
     * @createtime 2019/6/20 9:22
     */
    @requestmapping("/unauth")
    public map<string,object> unauth(){
        map<string,object> map = new hashmap<>();
        map.put("code",500);
        map.put("msg","未登录");
        return map;
    }
}

五.postman测试

登录成功后会返回token,因为是单点登录,再次登陆的话会返回新的token,之前redis的token就会失效了

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

当第一次访问接口后我们可以看到缓存中已经有权限数据了,在次访问接口的时候,shiro会直接去缓存中拿取权限,注意访问接口时候要设置请求头.

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

admin这个号现在没有sys:info:all这个权限的,所以无法访问getinfoall接口,我们要动态分配权限后,要清掉缓存,在访问接口时候,shiro会去重新执行授权方法,之后再次把权限和角色数据放入缓存中

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

访问添加权限测试接口,因为是测试,我把增加权限的用户admin写死在里面了,权限添加后,调用工具类清掉缓存,我们可以发现,redis中已经没有缓存了

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

再次访问getinfoall接口,因为缓存中没有数据,shiro会重新授权查询权限,拦截通过

springboot 整合shiro实现动态权限加载更新+session共享+单点登录

六.项目源码

https://gitee.com/liselotte/spring-boot-shiro-demo


https://github.com/xuyulong2017/my-java-demo

 

推荐阅读(点击即可跳转阅读)

 

1. 

2. 

3. 

4. 

5. 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网