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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值