shiro并发登录人数控制遇到的问题和解决
问题1:KickoutSessionControlFilter不起作用
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {}中设置:
{
…
filters.put(“kickout”, new KickoutSessionControlFilter());
…
filterMap.put("/**", “kickout”);
}
结果:KickoutSessionControlFilter不起作用。
进一步查看代码:
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroPermsFilterFactoryBean shiroFilter = new ShiroPermsFilterFactoryBean();
...
Map<String, Filter> filters = new HashMap<>(3);
filters.put("kickout", new KickoutSessionControlFilter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>(16);
...
filterMap.put("/**", "kickout");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
查看代码行:shiroFilter.setFilterChainDefinitionMap(filterMap);
setFilterChainDefinitionMap点进去:
public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
...
filterChainDefinitionMap.put("/**", "user");
super.setFilterChainDefinitionMap(filterChainDefinitionMap);
...
}
分析:
filterMap.put("/**", "kickout");
和
filterChainDefinitionMap.put("/**", "user");
操作的是同一个对象,
filterChainDefinitionMap是一个Map,
key一样,put会覆盖掉前面的值。
解决:
改
filterChainDefinitionMap.put("/**", "kickout,user");
问题2:KickoutSessionControlFilter中cache为null空指针异常
public class KickoutSessionControlFilter extends AccessControlFilter {
private Cache<String, Deque<Serializable>> cache;
...
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
...
Deque<Serializable> deque = cache.get(username);
...
deque = new LinkedList<Serializable>();
cache.put(username, deque);
...
}
}
查看:ShiroConfig.java中session管理器SessionManager
@Bean
public SessionManager sessionManager(GlobalProperties globalProperties){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setDeleteInvalidSessions(true);
if (globalProperties.isRedisSessionDao()) {
// 开启redis会话管理器
sessionManager.setSessionFactory(new UserSessionFactory());
sessionManager.setSessionDAO(new UserSessionDAO());
List<SessionListener> sessionListeners = new ArrayList<>();
sessionListeners.add(new UserSessionListener());
sessionManager.setSessionListeners(sessionListeners);
}
return sessionManager;
}
发现:框架没有配置CacheManager,有配置redis会话管理,改用RedisCacheManager。
解决:
public class KickoutSessionControlFilter extends AccessControlFilter {
private Cache<String, Deque<Serializable>> cache;
...
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
...
RedisCacheManager redisCacheManager = (RedisCacheManager) SpringContextUtils.getBean("redisCacheManager");
...
LinkedList<Serializable> linkedList = new LinkedList<Serializable>();
...
linkedList = (LinkedList<Serializable>) redisCacheManager.get(username);
...
redisCacheManager.set(username,linkedList);
...
}
}
问题3:服务器重启后首页访问:subject.getPrincipal()报ClassCastException异常
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
if(!subject.isAuthenticated() && !subject.isRemembered()) {
//如果没有登录,直接进行之后的流程
return true;
}
Session session = subject.getSession();
SysUserEntity user = (SysUserEntity) subject.getPrincipal();
username= user.getUsername();
...
}
一番打端点分析:
用户登陆状态下,服务器重启后:
subject.isAuthenticated()仍然为true。“//如果没有登录,直接进行之后的流程”该步骤不能进入,未return true,继续后续执行。
且session仍然在,未失效。
(SysUserEntity) subject.getPrincipal();
强转报异常,原因不明。
暂时解决方法:try捕获ClassCastException时调用subject.logout();登出。定向到首页。
try {
SysUserEntity user = (SysUserEntity) subject.getPrincipal();
username= user.getUsername();
} catch (ClassCastException cce) {
//服务器重启后,session仍然在,但subject.getPrincipal会有强转异常
try {
subject.logout();
} catch (Exception e) { //ignore
}
saveRequest(request);
httpServletRequest.setAttribute("errorMsg", "登录超时,请重新登录");
httpServletRequest.getRequestDispatcher("/login").forward(request, response);
return false;
}
系统环境
springboot
参考资料
https://blog.csdn.net/qq_33556185/article/details/51744004
https://www.w3cschool.cn/shiro/epht1ifg.html