shiro修改用户权限后并刷新对应用户缓存授权

shiro修改用户权限后并刷新缓存授权

在网上找了很多关于刷新缓存认证的文章,很多都是刷新自己的授权信息,本文案例为修改他人权限后,刷新他人的缓存授权信息。由于第一次写博文,写的不好还请手下留情,废话不多说,直接上干货

先看一下原理

先看一下我在自定义的Realm中认证的方法
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		Long userId = ShiroUtils.getUserId();
		MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
		Set<String> perms = menuService.listPerms(userId);//查询数据库里该用户有哪些权限
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setStringPermissions(perms);
		return info;//返回认证信息
	}
然后我们发起请求

在UserController中加上注解 @RequiresPermissions(“sys:user:add”)
需要用户拥有sys:user:add的权限

	@RequiresPermissions("sys:user:add")
	@Log("添加用户")
	@GetMapping("/add")
	String add(Model model) {
		service.add(model);
	}

shiro会帮我们拦截,查看是否拥有sys:user:add 的权限 核心方法

 AuthorizingRealm类的isPermitted(Permission permission, AuthorizationInfo info)方法
  protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
       AuthorizationInfo info = this.getAuthorizationInfo(principals);/获取info,info中保存的是用户的权限
        ....//比对用户的 info中是否拥有sys:user:add 的权限 
   }
   下面这个方法是原理的核心 
AuthorizingRealm类 getAuthorizationInfo(PrincipalCollection principals) //此方法获取info {
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            return null;
        } else {
            AuthorizationInfo info = null;
        	...
            Cache<Object, AuthorizationInfo> cache = this.getAvailableAuthorizationCache();//先去缓存去取用户的认证信息
            Object key;
            if (cache != null) {
              ....
                key = this.getAuthorizationCacheKey(principals);
                info = (AuthorizationInfo)cache.get(key);//在缓存中取到了认证信息
               ....
            }

            if (info == null) {
                info = this.doGetAuthorizationInfo(principals);//如果缓存中没有的话,走这个方法,这个方法是个抽象方法,需要自己实现 ,我们自定义的realm中已经实现此方法,在最上面的代码中
                if (info != null && cache != null) {
                 ....
                    key = this.getAuthorizationCacheKey(principals);// 查询到info后,再存到缓存中
                    cache.put(key, info);
                }
            }

            return info;
        }
    }
}


小结: 原理就是第一次查询AuthorizationInfo 保存到了缓存中, 我们刷新的实现就是修改了用户的权限后 删除掉缓存中的对应用户的认证信息。而且我们已经找到了key, 然后直接删除就OK了

代码

在ShiroUtil 类中定义删除方法
public class ShiroUtils {
//注意:此处sessionDao 是static  不能使用@autowired 自动注入,网上有教程注入的方法,自己测了一下并没有成功
我们使用此方法来获取,
    private static RedisSessionDAO sessionDAO = SpringUtil.getBean(RedisSessionDao.class);

    public static Subject getSubjct() {
        return SecurityUtils.getSubject();
    }
    /**
     * 获取当前用户信息
     * @Return SysUserEntity 用户信息
     */
    public static UserDO getUser() {
        Object object = getSubjct().getPrincipal();
        return (UserDO)object;
    }
    public static Long getUserId() {
        return getUser().getUserId();
    }
    public static void logout() {
        getSubjct().logout();
    }

    public static List<Principal> getPrinciples() {
        List<Principal> principals = null;
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        return principals;
    }

    /**
     * 获取当前用户Session
     * @Return SysUserEntity 用户信息
     */
    public static Session getSession() {
        return SecurityUtils.getSubject().getSession();
    }
    /**
     * 删除用户缓存信息
     * @Param  username  用户名称
     * @Param  isRemoveSession 是否删除Session,删除后用户需重新登录
     */
    public static void deleteCache(String username, boolean isRemoveSession){
        //从缓存中获取Session
        Session session = null;
        // 获取当前已登录的用户session列表
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        UserDO sysUserEntity;
        Object attribute = null;
        // 遍历Session,找到该用户名称对应的Session
        for(Session sessionInfo : sessions){
            attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            sysUserEntity = (UserDO) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (sysUserEntity == null) {
                continue;
            }
            if (Objects.equals(sysUserEntity.getUsername(), username)) {
                session=sessionInfo;
                // 清除该用户以前登录时保存的session,强制退出  -> 单用户登录处理
                if (isRemoveSession) {
                    sessionDAO.delete(session);
                }
            }
        }

        if (session == null||attribute == null) {
            return;
        }
        //删除session
        if (isRemoveSession) {
            sessionDAO.delete(session);
        }
        //删除Cache,再访问受限接口时会重新授权
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        Authenticator authc = securityManager.getAuthenticator();
        ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
    }

    /**
     * 从缓存中获取指定用户名的Session
     * @param username
     */
    private static Session getSessionByUsername(String username){
        // 获取当前已登录的用户session列表
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        UserDO user;
        Object attribute;
        // 遍历Session,找到该用户名称对应的Session
        for(Session session : sessions){
            attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            user = (UserDO) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (user == null) {
                continue;
            }
            if (Objects.equals(user.getUsername(), username)) {
                return session;
            }
        }
        return null;
    }
}
其中 redisSessionDao 是 shiro-redis包下的类

这里贴一下依赖

        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
        </dependency>
springUtil也贴一下吧
@Component
public class SpringUtil implements ApplicationContextAware {
    private static ApplicationContext context;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    
    public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }

    public static <T> T getBean(String beanName) {
        return (T) context.getBean(beanName);
    }
}

做到这里准备工作就完成了, 我们开始走起

模拟userService updateUser方法


  public int update(User user) {
        
        int r = userMapper.update(user);
        ShiroUtils.deleteCache(user.getUsername(),false);//更新用户并删除缓存
        return r;
    }

写的比较粗糙, 希望可以帮到大家, 有疑问也可以给我留言,也欢迎大家批评指正。。。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值