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

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

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

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

Java代码  
  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配置

Java代码  
  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核心代码:

Java代码  
  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

Shiro学习(18)并发人数限制的更多相关文章

  1. Apache shiro学习总结

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  2. Shiro学习

    Shiro学习资源 Shiro官网,http://shiro.apache.org/index.html 学习网站链接,http://blog.java1234.com/blog/articles/4 ...

  3. Shiro学习笔记(5)——web集成

    Web集成 shiro配置文件shiroini 界面 webxml最关键 Servlet 測试 基于 Basic 的拦截器身份验证 Web集成 大多数情况.web项目都会集成spring.shiro在 ...

  4. 如何才能够系统地学习Java并发技术?

    微信公众号[Java技术江湖]一位阿里Java工程师的技术小站 Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容. 这里不仅仅是指使用简单的多线程编程,或者使用juc的某个类.当然这些 ...

  5. Apache Shiro学习-2-Apache Shiro Web Support

     Apache Shiro Web Support  1. 配置 将 Shiro 整合到 Web 应用中的最简单方式是在 web.xml 的 Servlet ContextListener 和 Fil ...

  6. shiro学习笔记_0600_自定义realm实现授权

    博客shiro学习笔记_0400_自定义Realm实现身份认证 介绍了认证,这里介绍授权. 1,仅仅通过配置文件来指定权限不够灵活且不方便.在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息 ...

  7. Shiro学习笔记总结,附加" 身份认证 "源码案例(一)

    Shiro学习笔记总结 内容介绍: 一.Shiro介绍 二.subject认证主体 三.身份认证流程 四.Realm & JDBC reaml介绍 五.Shiro.ini配置介绍 六.源码案例 ...

  8. SpringBoot+Shiro学习(七):Filter过滤器管理

    SpringBoot+Shiro学习(七):Filter过滤器管理 Hiwayz 关注  0.5 2018.09.06 19:09* 字数 1070 阅读 5922评论 1喜欢 20 先从我们写的一个 ...

  9. 从源码学习Java并发的锁是怎么维护内部线程队列的

    从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...

  10. 如何深入学习Java并发编程?

    在讲解深入学习Java并发编程的方法之前,先分析如下若干错误的观点和学习方法. 错误观点1:学习Java编程主要是学习多线程. 这话其实是说明了表面现象,多线程其实还真是并发编程的实现方式,但在实际高 ...

随机推荐

  1. C#中using语句是什么意思

    使用using语句最终生成的其实是一个try, finally代码块,在finally代码块里释放资源.要求是:为 using 语句提供的对象必须实现 IDisposable 接口.此接口提供了 Di ...

  2. SSH出错

    [root@node01 ~]# ssh node02 ssh_exchange_identification: Connection closed by remote host 修改连接数无效 [r ...

  3. python进阶:类和对象

    @修饰符:将被修饰的函数作为参数,运行修饰函数 实例方法: 静态方法:@staticmethod 类方法:@classmethod 自省:通过一定的机制查询到对象的内部结构 序列类: 列表推导式(例表 ...

  4. java并发编程笔记(五)——线程安全策略

    java并发编程笔记(五)--线程安全策略 不可变得对象 不可变对象需要满足的条件 对象创建以后其状态就不能修改 对象所有的域都是final类型 对象是正确创建的(在对象创建期间,this引用没有逸出 ...

  5. JS-监听整个页面上的DOM树变化

    # [在线预览](https://jsfiddle.net/1010543618/fyf913t0/) ## 方法 - 使用<Web API 接口>的<MutationObserve ...

  6. 2017《Java技术》预备作业 计科1502任秀兴

    阅读邹欣老师的博客,谈谈你期望的师生关系是什么样的? 我认为,学生和老师的关系,应该亦师亦友.可以以一种朋友的身份去进行教学,是我们理想中的课堂. 在生活中,老师和我们应该多沟通,成为朋友,在有感情的 ...

  7. Rust <1>:数据类型、变量、可变性、常量、隐藏

    rust 是强类型语言,所有变量.常量都必须有明确的数据类型:很多情况下,省略类型声明,编译器可自动推导,但不是所有情况下都会成功. rust 有整型.浮点型.布尔型.字符型.数组.元组.枚举.结构体 ...

  8. centos7 编译打包bcache-tools

    centos7 build bcache-tools 获取源码 centos 本身不提供bcache-tools的rpm,所以需要自己build. 从fedora下载源码,也可以从github社区下载 ...

  9. CentOS 7 64位虚拟机安装过程

    第一步:新建一个虚拟机,选择典型安装,点击下一步.

  10. JFreeChart教程

    图表是信息的图形表示.有可用的各种工具,它可用于创建不同类型的图表. 本教程学习什么是JFreeChart?为什么需要它,并在各种方式列出一个基于Java的应用程序或独立创建不同类型的图表. JFre ...