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

<!-- session 校验单个用户是否多次登录 --><bean id="kickoutSessionFilter"  class="cn.easted.edm.core.security.KickoutSessionFilter">    <property name="sessionManager" ref="sessionManager"/>    <property name="cacheManager" ref="shiroEhcacheManager"/>  <property name="kickoutAfter" value="false"/>    <property name="maxSession" value="1"/>    <property name="kickoutUrl" value="/error/500"/>    </bean> 

cacheManager:使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
sessionManager:用于根据会话ID,获取会话进行踢出操作的;
kickoutAfter:是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
maxSession:同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
kickoutUrl:被踢出后重定向到的地址;

shiroFilter配置 :

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login"/><property name="successUrl" value="/dashboard/list"/><property name="unauthorizedUrl" value="/401"/><property name="filters">  <map>  <entry key="authc">  <bean  class="cn.easted.edm.core.security.RoleAuthorizationFilter" />  </entry>  <entry key="roleOrFilter" value-ref="roleOrFilter"/><entry key="kickout" value-ref="kickoutSessionFilter"></entry></map> </property><property name="filterChainDefinitions"><value><!--角色认证-->/desktop/edit=authc,kickout,roleOrFilter["operator,admin"]</value></property></bean>

KickoutSessionControlFilter核心代码:

package cn.easted.edm.core.security;import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;/*** 踢出用户过滤器* @ClassName:KickoutSessionFilter* @author:Wanghao* @date: 2017年9月29日 下午3:24:15*/
public class KickoutSessionFilter extends AccessControlFilter{private String kickoutUrl; //踢出后到的地址  private boolean kickoutAfter; //踢出之前登录的/之后登录的用户,默认踢出之前登录的用户  private int maxSession; //同一个帐号最大会话数 默认1  private SessionManager sessionManager;  private Cache<String, Deque<Serializable>> cache; @Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)throws Exception {return false;}@Overrideprotected 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<Serializable> deque = cache.get(username);  if (deque == null) {  deque = new LinkedList<Serializable>();  cache.put(username, deque);  }  //如果队列里没有此sessionId,且用户没有被踢出;放入队列  if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) {  deque.push(sessionId);  }  //如果队列里的sessionId数超出最大会话数,开始踢人  while (deque.size() > maxSession) {  Serializable kickoutSessionId = null;  if (kickoutAfter) { //如果踢出后者  kickoutSessionId=deque.getFirst();  kickoutSessionId = deque.removeFirst();  } else { //否则踢出前者  kickoutSessionId = deque.removeLast();  }  try {  Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));  if(kickoutSession != null) {  //设置会话的kickout属性表示踢出了  kickoutSession.setAttribute("kickout", true);  }  } catch (Exception e) {e.printStackTrace();  }  }  //如果被踢出了,直接退出,重定向到踢出后的地址  if (session.getAttribute("kickout") != null) {  //会话被踢出了  try {  subject.logout();  } catch (Exception e) {   }
//          throw new CommonException("101", "您已被迫下线,若不是您本人操作,请立即修改密码!");HttpServletResponse res = (HttpServletResponse) response;res.setStatus(101);res.sendRedirect(kickoutUrl);
//          RoleAuthorizationFilter.sendFastJson(response, "您已被迫下线,若不是您本人操作,请立即修改密码!");
//          WebUtils.issueRedirect(request, response, kickoutUrl); return false;  }  return true;  }  public void setCacheManager(CacheManager cacheManager) {  this.cache = cacheManager.getCache("shiro-activeSessionCache");  }  /*** @param kickoutUrl the kickoutUrl to set*/public void setKickoutUrl(String kickoutUrl) {this.kickoutUrl = kickoutUrl;}/*** @param kickoutAfter the kickoutAfter to set*/public void setKickoutAfter(boolean kickoutAfter) {this.kickoutAfter = kickoutAfter;}/*** @param maxSession the maxSession to set*/public void setMaxSession(int maxSession) {this.maxSession = maxSession;}/*** @param sessionManager the sessionManager to set*/public void setSessionManager(SessionManager sessionManager) {this.sessionManager = sessionManager;}/*** @Title: getKickoutUrl <BR>* @return:String <BR>*/public String getKickoutUrl() {return kickoutUrl;}/*** @Title: isKickoutAfter <BR>* @return:boolean <BR>*/public boolean isKickoutAfter() {return kickoutAfter;}/*** @Title: getMaxSession <BR>* @return:int <BR>*/public int getMaxSession() {return maxSession;}/*** @Title: getSessionManager <BR>* @return:SessionManager <BR>*/public SessionManager getSessionManager() {return sessionManager;}/*** @Title: getCache <BR>* @return:Cache<String,Deque<Serializable>> <BR>*/public Cache<String, Deque<Serializable>> getCache() {return cache;}/*** @param cache the cache to set*/public void setCache(Cache<String, Deque<Serializable>> cache) {this.cache = cache;}
}

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

shiro并发登录人数控制相关推荐

  1. Shiro并发登录人数控制遇到的问题和解决

    shiro并发登录人数控制遇到的问题和解决 问题1:KickoutSessionControlFilter不起作用 问题2:KickoutSessionControlFilter中cache为null ...

  2. springboot + shiro 尝试登录次数限制与并发登录人数控制

    源码项目地址 尝试登录次数控制实现 实现原理 Realm在验证用户身份的时候,要进行密码匹配.最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatche ...

  3. SpringBoot 实现并发登录人数控制

    作者丨殷天文 www.jianshu.com/p/b6f5ec98d790 今天跟大家分享SpringBoot 实现并发登录人数控制的知识. 1 SpringBoot 实现并发登录人数控制 通常系统都 ...

  4. 模仿爱奇艺账号登录限制人数,SpringBoot 并发登录人数控制,踢人功能

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

  5. SpringBoot 并发登录人数控制

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:殷天文 www.jianshu.com/p/b6f5ec98d790 技术经验交流:点击入 ...

  6. 如何用 SpringBoot 实现并发登录人数控制(附代码)

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 作者 ...

  7. springboot + shiro之登录人数限制、登录判断重定向、session时间设置

    springboot + shiro之登录人数控制 项目 前篇:spring boot + mybatis + layui + shiro后台权限管理系统:https://blog.51cto.com ...

  8. 厉害了,教你用 Spring Boot 控制并发登录人数

    作者:殷天文 www.jianshu.com/p/b6f5ec98d790 通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功 ...

  9. 【Shiro】7、Shiro实现控制用户并发登录并踢人下线

    在传统的项目中,同一账户是允许多人同时登录在线的,有的使用场景恰恰是不允许多人同时在线的,那么我们可以通过 Shiro 来控制并发登录,并实现后登录的用户,挤掉前面登录的用户 1.并发登录过滤器 pa ...

最新文章

  1. python opencv 检测特定颜色
  2. C编译器、链接器、加载器详解
  3. 详解log4j2(上) - 从基础到实战
  4. aix 5.3 安装oracle 10g r2,在AIX5上安装ORACLE10G R2的步骤
  5. 台式计算机总是重启,台式电脑经常自动重启怎么修复
  6. Vbs压缩备份文件夹以日期命名
  7. 谷歌 Chrome 同步功能可滥用于 C2 通信及数据提取
  8. Python开发第一步的环境搭建
  9. Atitit 图像处理 opencv使用蒙版mask抠图 切割 attilax总结
  10. ajax 解析gzip,javascript – 如何让浏览器gunzip一个Ajax获取gziped文本文件?
  11. 运行时库(runtime library)
  12. My God,CImage裁剪图片变成黑色了
  13. U盘写保护,不能被格式化
  14. 【机器学习】【数学推导】神经网络(NN)及误差逆传播(BP详细推导过程)
  15. CST仿真指导 | 利用软件自带的示例与教程快速建模仿真
  16. 计算机网络拓扑结构 教案,计算机网络拓扑结构获奖教案.docx
  17. UG NX二次开发(C#)-装配-替换组件
  18. 贝多芬交响曲全集(转)
  19. python day8
  20. WINCE上网本才是正道——关于目前ARM+CE的上网本的文章汇编

热门文章

  1. windows暴力删除文件
  2. CC00004.CloudKubernetes——|Kuberneteskubeadm部署.V03|——|kubernetes集群部署|
  3. 深入浅出AOF功能和AOF重写两个知识点
  4. Ubuntu 安装lua
  5. 腾讯云“凶猛”,ToB、ToG都能打
  6. 一文带你了解什么是CNCF云原生
  7. 【餐厅点餐平台|二】总体设计
  8. Unity Protobuf 简单 案例 详解 从下载到安装和使用
  9. html怎么做小米logo,css3实现小米的logo
  10. 虚拟dom (virtual dom)(vnode)