作者:changeverything77_262 | 来源:互联网 | 2024-12-06 17:14
前言:本篇文章将引导您了解并掌握如何在Apache Shiro框架中实现并发登录人数的限制功能,这对于确保系统的安全性和用户体验至关重要。
目录链接:Shiro学习指南
在实际应用开发中,有时需要限制同一账号的同时在线数量,例如,一个账号在同一时间只能由一个用户登录,或者允许多个用户同时登录但不超过特定的数量。这种需求可以通过Shiro提供的灵活配置和扩展机制来实现,尽管Shiro本身并没有直接提供这项功能。
本示例基于前一章节的综合实例,通过扩展Shiro的Filter机制,具体来说是通过创建一个名为KickoutSessionControlFilter的自定义过滤器来实现这一功能。
配置使用(spring-config-shiro.xml)
KickoutSessionControlFilter用于管理和限制用户的并发登录数:
- class="com.github.zhangkaitao.shiro.chapter18.web.shiro.filter.KickoutSessionControlFilter">
参数解释:
cacheManager:利用缓存管理器存储用户的会话信息,以便于追踪同一用户的不同会话。
sessionManager:负责会话管理,能够根据会话ID检索和操作会话。
kickoutAfter:决定当达到最大会话限制时,是踢出新登录的用户还是旧的会话,默认为false,意味着优先保留旧的会话。
maxSession:设定单个用户允许的最大并发会话数,默认值为1。
kickoutUrl:用户被踢出后重定向至的URL。
ShiroFilter配置
- /login = authc
- /logout = logout
- /authenticated = authc
- /** = kickout,user,sysUser
以上配置确保了除登录页面外的所有请求都会经过kickout过滤器,以检查并发登录情况。
测试场景
为了验证功能,可以尝试使用三个不同类型的浏览器(如IE、Chrome和Firefox)同时登录同一个账户。当第三个用户登录时,最早登录的用户会被自动登出,并重定向到指定的踢出页面。
核心代码解析
- 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();
- String username = (String) subject.getPrincipal();
- Serializable sessiOnId= session.getId();
- Deque deque = cache.get(username);
- if (deque == null) {
- deque = new LinkedList();
- cache.put(username, deque);
- }
- if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
- deque.push(sessionId);
- }
- while (deque.size() > maxSession) {
- Serializable kickoutSessiOnId= null;
- if (kickoutAfter) { // 踢出后登录的用户
- kickoutSessiOnId= deque.removeFirst();
- } else { // 踢出先登录的用户
- kickoutSessiOnId= deque.removeLast();
- }
- try {
- Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
- if (kickoutSession != null) {
- kickoutSession.setAttribute("kickout", true);
- }
- } catch (Exception e) {}
- }
- if (session.getAttribute("kickout") != null) {
- try {
- subject.logout();
- } catch (Exception e) {}
- saveRequest(request);
- WebUtils.issueRedirect(request, response, kickoutUrl);
- return false;
- }
- return true;
- }
上述代码展示了如何通过缓存机制管理用户的会话列表,并在达到最大会话数时执行踢出逻辑。对于大规模的应用,建议考虑将缓存数据持久化到数据库或其他支持持久化的缓存服务中,以提高系统性能和稳定性。
此外,还可以参考其他开源项目中的实现,例如JavaEE项目开发脚手架中的用户在线管理模块,该模块提供了后台手动踢出用户的功能:
https://github.com/zhangkaitao/es/blob/master/web/src/main/java/com/sishuok/es/sys/user/web/controller/UserOnlineController.java
更多示例代码和深入讨论,请访问GitHub仓库:https://github.com/zhangkaitao/shiro-example,并欢迎加入技术交流群 231889722 探讨Spring和Shiro相关技术。