目录贴:跟我学Shiro目录贴

在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录;要么踢出前者登录(强制退出)。比如spring security就直接提供了相应的功能;Shiro的话没有提供默认实现,不过可以很容易的在Shiro中加入这个功能。

示例代码基于《第十六章 综合实例》完成,通过Shiro Filter机制扩展KickoutSessionControlFilter完成。

首先来看看如何配置使用(spring-config-shiro.xml

kickoutSessionControlFilter用于控制并发登录人数的

  1. <bean id="kickoutSessionControlFilter"
  2. class="com.github.zhangkaitao.shiro.chapter18.web.shiro.filter.KickoutSessionControlFilter">
  3. <property name="cacheManager" ref="cacheManager"/>
  4. <property name="sessionManager" ref="sessionManager"/>
  5. <property name="kickoutAfter" value="false"/>
  6. <property name="maxSession" value="2"/>
  7. <property name="kickoutUrl" value="/login?kickout=1"/>
  8. </bean>

cacheManager:使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;

sessionManager:用于根据会话ID,获取会话进行踢出操作的;

kickoutAfter:是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;

maxSession:同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;

kickoutUrl:被踢出后重定向到的地址;

shiroFilter配置

  1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  2. <property name="securityManager" ref="securityManager"/>
  3. <property name="loginUrl" value="/login"/>
  4. <property name="filters">
  5. <util:map>
  6. <entry key="authc" value-ref="formAuthenticationFilter"/>
  7. <entry key="sysUser" value-ref="sysUserFilter"/>
  8. <entry key="kickout" value-ref="kickoutSessionControlFilter"/>
  9. </util:map>
  10. </property>
  11. <property name="filterChainDefinitions">
  12. <value>
  13. /login = authc
  14. /logout = logout
  15. /authenticated = authc
  16. /** = kickout,user,sysUser
  17. </value>
  18. </property>
  19. </bean>

此处配置除了登录等之外的地址都走kickout拦截器进行并发登录控制。

测试

此处因为maxSession=2,所以需要打开3个浏览器(需要不同的浏览器,如IE、Chrome、Firefox),分别访问http://localhost:8080/chapter18/进行登录;然后刷新第一次打开的浏览器,将会被强制退出,如显示下图:

KickoutSessionControlFilter核心代码:

  1. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
  2. Subject subject = getSubject(request, response);
  3. if(!subject.isAuthenticated() && !subject.isRemembered()) {
  4. //如果没有登录,直接进行之后的流程
  5. return true;
  6. }
  7. Session session = subject.getSession();
  8. String username = (String) subject.getPrincipal();
  9. Serializable sessionId = session.getId();
  10. //TODO 同步控制
  11. Deque<Serializable> deque = cache.get(username);
  12. if(deque == null) {
  13. deque = new LinkedList<Serializable>();
  14. cache.put(username, deque);
  15. }
  16. //如果队列里没有此sessionId,且用户没有被踢出;放入队列
  17. if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
  18. deque.push(sessionId);
  19. }
  20. //如果队列里的sessionId数超出最大会话数,开始踢人
  21. while(deque.size() > maxSession) {
  22. Serializable kickoutSessionId = null;
  23. if(kickoutAfter) { //如果踢出后者
  24. kickoutSessionId = deque.removeFirst();
  25. } else { //否则踢出前者
  26. kickoutSessionId = deque.removeLast();
  27. }
  28. try {
  29. Session kickoutSession =
  30. sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
  31. if(kickoutSession != null) {
  32. //设置会话的kickout属性表示踢出了
  33. kickoutSession.setAttribute("kickout", true);
  34. }
  35. } catch (Exception e) {//ignore exception
  36. }
  37. }
  38. //如果被踢出了,直接退出,重定向到踢出后的地址
  39. if (session.getAttribute("kickout") != null) {
  40. //会话被踢出了
  41. try {
  42. subject.logout();
  43. } catch (Exception e) { //ignore
  44. }
  45. saveRequest(request);
  46. WebUtils.issueRedirect(request, response, kickoutUrl);
  47. return false;
  48. }
  49. return true;
  50. }

此处使用了Cache缓存用户名—会话id之间的关系;如果量比较大可以考虑如持久化到数据库/其他带持久化的Cache中;另外此处没有并发控制的同步实现,可以考虑根据用户名获取锁来控制,减少锁的粒度。

另外可参考JavaEE项目开发脚手架,其提供了后台踢出用户的功能:

https://github.com/zhangkaitao/es/blob/master/web/src/main/java/com/sishuok/es/sys/user/web/controller/UserOnlineController.java

示例源代码:https://github.com/zhangkaitao/shiro-example;可加群 231889722 探讨Spring/Shiro技术。

第十八章 并发登录人数控制——《跟我学Shiro》的更多相关文章

  1. 2017.4.12 开涛shiro教程-第十八章-并发登录人数控制

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第十八章-并发登录人数控制 shiro中没有提 ...

  2. 2017.6.30 用shiro实现并发登录人数控制(实际项目中的实现)

    之前的学习总结:http://www.cnblogs.com/lyh421/p/6698871.html 1.kickout功能描述 如果将配置文件中的kickout设置为true,则在另处再次登录时 ...

  3. SpringBoot 并发登录人数控制

    通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能 dem ...

  4. 并发登录人数控制--Shiro系列(二)

    为了安全起见,同一个账号理应同时只能在一台设备上登录,后面登录的踢出前面登录的.用Shiro可以轻松实现此功能. shiro中sessionManager是专门作会话管理的,而sessinManage ...

  5. 第十六章 综合实例——《跟我学Shiro》

    简单的实体关系图 简单数据字典 用户(sys_user) 名称 类型 长度 描述 id bigint 编号 主键 username varchar 100 用户名 password varchar 1 ...

  6. 第二十二章 集成验证码——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 在做用户登录功能时,很多时候都需要验证码支持,验证码的目的是为了防止机器人模拟真实用户登录而恶意访问,如暴力破解用户密码/恶意评论等.目前也有一些验证码比较简单,通过一 ...

  7. 十六章 综合实例——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 简单的实体关系图 简单数据字典 用户(sys_user) 名称 类型 长度 描述 id bigint 编号 主键 username varchar 100 用户名 pa ...

  8. 第八章 拦截器机制——《跟我学Shiro》

    转发地址:https://www.iteye.com/blog/jinnianshilongnian-2025656 博客分类: 跟我学Shiro 跟我学Shiro  目录贴:跟我学Shiro目录贴 ...

  9. TCP/IP详解 卷1 第十八章 TCP的建立与终止

    第十八章 TCP的建立与终止 tcpdump Tcpdump可以将网络中传送的数据报完截获下来进行分析.它支持针对网络层.协议.主机.网络或端口的过滤,并提供and.or.not等逻辑语句来帮助你去掉 ...

随机推荐

  1. vue 安装 ‘node-sass’ 运行报错:ERROR in Cannot find module 'node-sass'

    好像是由于cnpm安装导致.执行下面的安装代码,或者使用npm 安装node-sass cnpm install node-sass@latest

  2. 判断大文件是否上传成功(一个大文件上传到ftp,判断是否上传完成)

    大文件上传ftp,不知道有没有上传完成,如果没有上传完成另一个程序去下载这个文件,导致下载不完整. 判断一个文件是否上传完成的方法: /** * 间隔一段时间去计算文件的长度来判断文件是否写入完成 * ...

  3. 使用Spring PropertyPlaceholderConfigurer 配置中文出现乱码的解决方法

    在使用org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 读取配置文件时,发现对于中文的处理会出现乱码现象,比 ...

  4. [codevs]线段树练习5

    http://codevs.cn/problem/4927/ #include <iostream> #include <cstdio> #include <algori ...

  5. link cut tree 洞穴勘测

    /*[bzoj2049][Sdoi2008]Cave 洞穴勘测 2014年7月30日1,06923Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区 . ...

  6. 斐波那契数列 矩阵乘法优化DP

    斐波那契数列 矩阵乘法优化DP 求\(f(n) \%1000000007​\),\(n\le 10^{18}​\) 矩阵乘法:\(i\times k\)的矩阵\(A\)乘\(k\times j\)的矩 ...

  7. centos7的redis加哨兵系统

    三台服务器 1.下载 wget http://download.redis.io/releases/redis-5.0.3.tar.gztar -zxvf redis-5.0.3.tar.gzcd r ...

  8. c++ 珊格画椭圆

    #ifndef _TEST_H #define _TEST_H #include <iostream> #include <math.h> using namespace st ...

  9. FOI 冬令营 Day6

    目录 T1.堆(heap) 传送门 Code T2.密文(secret) 传送门 Code T3.树(tree) 传送门 Code 别问Day5到底去哪里了,咕咕咕 T1.堆(heap) 传送门 Co ...

  10. python3编程基础之一:标识符

    每种编程语言都是需要处理数据的,需要变量.函数.类等,而这些都是通过名称访问的.因此,能够作为变量.函数.类等名称的字符串就是标识符.数据,是计算机进行运算的实体.标识符,用来标记的符号,它会指向一个 ...