在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录;要么踢出前者登录(强制退出)。比如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. (转)Uncaught TypeError: Cannot set property 'innerHTML' of null

    (转)http://www.cnblogs.com/Ricky-Huang/p/5536253.html 在使用Ueditor的时候,会爆出这样的错误: 浏览器控制台就报错了 Cannot set p ...

  2. Java中的小知识。

    package jicheng; public class Animal { //定义一个成员变量name. private String name; public String getName() ...

  3. C# GDI+简单绘图

    一.使用Pen画笔 Pen的主要属性有: Color(颜色),DashCap(短划线终点形状),DashStyle(虚线样式),EndCap(线尾形状), StartCap(线头形状),Width(粗 ...

  4. Centos7 安装配置 SVN

    准备工作: 检查是否安装SVN:rpm -qa subversion 查看安装SVN版本信息:svnserve --version 卸载旧版本SVN: yum remove subversion 1. ...

  5. bzoj 3881 [Coci2015]Divljak——LCT维护parent树链并

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3881 对 S 建 SAM ,每个 T 会让 S 的 parent 树的链并答案+1:在 T ...

  6. [CSP-S模拟测试62]题解

    A.Graph 因为点可以随便走,所以对于每个联通块,答案为边数/2向下取整. 用类似Tarjan的方式,对于每个联通块建立一棵搜索树,尽量让每一个节点的儿子两两配对,如果做不到就用上头顶的天线. # ...

  7. 【从0到1,搭建Spring Boot+RESTful API+Shiro+Mybatis+SQLServer权限系统】04、统一处理异常

    本节讨论如何使用Spring的异常处理机制,当我们程序出现错误时,以相同的一种格式,把错误信息返回给客户端 1.创建一些自定义异常 public class TipsException extends ...

  8. (转)图文详解MyEclipse中新建Maven webapp项目的步骤

    转:https://blog.csdn.net/ClementAD/article/details/46592557 MyEclipse中创建新的Maven项目(webapp目录结构)过程如下:   ...

  9. MySQL strcmp 函数

    STRCMP(str1, str2) 比较两个字符串,如果这两个字符串相等返回0,如果第一个参数是根据当前的排序小于第二个参数顺序返回-1,否则返回1. mysql> SELECT STRCMP ...

  10. shell变量相关知识

    环境变量和普通变量 一.几个常用命令: 1. set : 输出所有变量,包含全局变量和局部变量 2. env : 只显示全局变量 3. declare : 输出所有的变量,函数,整数和已经导出的变量 ...