shiro并发登录人数控制遇到的问题和解决
- 问题1:KickoutSessionControlFilter不起作用
- 问题2:KickoutSessionControlFilter中cache为null空指针异常
- 问题3:服务器重启后首页访问:subject.getPrincipal()报ClassCastException异常
- 系统环境
- 参考资料
问题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;...@Overrideprotected 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
@Beanpublic 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;...@Overrideprotected 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异常
@Overrideprotected 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