shiro用户信息自动更新并删除授权缓存
业务场景
一个权限管理系统下需要切换系统空间,此时需要更新登录用户在不同空间下的用户信息。
一个用户在一个空间下会拥有特定的功能权限,因此切换空间的动作会引起当前登录用户功能权限变更。
思路
通过shiro框架进行登录用户权限管理
- 每个登录用户在shiro中都使用Subject来标识
- 根据subject获取principal
- 将principal反序列化为存储用户信息的POJO对象
- 修改对象中的某项参数达成更新不同空间下用户信息的需求
- 删除用户之前的授权缓存,让下一次鉴权时自动触发授权方法更新用户权限信息
方案
- 通过
SecurityUtils.getSubject()
可获取当前登录用户的subject subject.getPrincipal()
获取当前登录用户的标识信息- 通过json反序列化获取对象POJO(这一步也可根据之前在
doGetAuthenticationInfo
方法中生成的principal进行强转等方式完成 - 通过新的
principal
生成PrincipalCollection
- 调用
subject.runAs
切换身份 - 借助
Authenticator
的实现类CachingRealm
在登出时执行的doClearCache(PrincipalCollection principals)
来清空授权缓存
代码
@Override
public List<TreeVO> switchSystem(Integer appId) {
// 用户切换到指定系统,类似用户切换,将返回新的menus信息
// 需执行用户重新登录/授权的操作,或者一次性将用户不同系统的所有权限给出,其实主要是为了变更缓存的用户信息(用户所在系统空间)
// 更新用户信息
StaffLoginVo validStaffInfo = loginCacheUtil.getValidStaffInfo();
log.info("before update: " + validStaffInfo);
validStaffInfo.setAppId(appId);
Subject subject = SecurityUtils.getSubject();
String newLoginInfo = JSON.toJSONString(validStaffInfo);
PrincipalCollection principals = subject.getPrincipals();
String realmName = principals.getRealmNames().iterator().next();
// 生成新的principal
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(newLoginInfo, realmName);
// 用户身份切换
subject.runAs(newPrincipalCollection);
log.info("after update: " + loginCacheUtil.getValidStaffInfo());
// 重新授权
// loginCacheUtil.deleteCache(validStaffInfo.getUsername(),false);
// 需要根据变更前的principals删除之前的授权信息
loginCacheUtil.deleteCacheBySimplePrincipalCollection((SimplePrincipalCollection) principals);
// 返回用户信息更新后的菜单数据
return sysMenuPermissionService.listMenus();
}
/**
* 根据principalCollection删除授权缓存
* @param attribute
*/
public void deleteCacheBySimplePrincipalCollection(SimplePrincipalCollection attribute) {
//删除Cache,再访问受限接口时会重新授权
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
Authenticator authc = securityManager.getAuthenticator();
((LogoutAware) authc).onLogout(attribute);
}