shiro修改用户权限后并刷新对应用户缓存授权
程序员文章站
2022-07-15 13:24:41
...
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;
}
写的比较粗糙, 希望可以帮到大家, 有疑问也可以给我留言,也欢迎大家批评指正。。。