2017.6.30 用shiro实现并发登录人数控制(实际项目中的实现)
之前的学习总结:http://www.cnblogs.com/lyh421/p/6698871.html
1.kickout功能描述

2.kickout的实现
2.1 新建KickoutSessionControlFilter extends AccessControlFilter
详细的方法实现,后面再来完成。类存放于公共module:base_project中。
1 public class KickoutSessionControlFilter extends AccessControlFilter {
2 @Override
3 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
4 return false;
5 }
6
7 @Override
8 protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
9 return false;
10 }
11 }
2.2 配置spring-config-shiro.xml
这两个文件配置在要使用kickout功能的module中。
(1)kickoutSessionControllerFilter
kickoutAfter:是否提出后来登录的,默认为false,即后来登录的踢出前者。
maxSession:同一个用户的最大会话数,默认1,表示同一个用户最多同时一个人登录。
kickoutUrl:被踢出后重定向的地址。
<!--并发登录控制-->
<bean id="kickoutSessionControlFilter" class="***.common.filter.KickoutSessionControlFilter">
<property name="cacheManager" ref="springCacheManager"/>
<property name="kickoutAfter" value="false"/>
<property name="maxSession" value="1"/>
<property name="kickoutUrl" value="/login.do"/>
</bean>
(2)shiroFilter
此处配置什么时候走kickout 拦截器,进行并发登录控制。这里拦截所有.jsp和.do的路径。
<bean id="AuthRequestFilter" class="com.baosight.aas.auth.filter.AuthRequestFilter"/>
<!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->
<!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 -->
<bean id="shiroFilter" class="com.baosight.aas.auth.filter.factory.ClientShiroFilterFactoryBean">
//略
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
//略
<entry key="kickout" value-ref="kickoutSessionControlFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
//略 /**/*.jsp = forceLogout,authc,kickout
/**/*.do = forceLogout,authc,kickout
/** = forceLogout,authc
</value>
</property>
</bean>
2.3 ehcache.xml
注意,其他module在配置shiro的时候,都是使用的公共module:base_project中的ehcache.xml文件。在此文件中加上一段:
这里的名称shiro-kickout-session在后面的kickoutController里要用到。
<cache name="shiro-kickout-session"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
2.4 实现KickoutSessionControlFilter
package com.baosight.common.filter; import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList; /**
* Created by liyuhui on 2017/4/12.
*/
public class KickoutSessionControlFilter extends AccessControlFilter{
private static final Logger LOGGER = LoggerFactory.getLogger(KickoutSessionControlFilter.class); @Value("${aas.kickout:false}")
private String kickout; private String kickoutUrl;
private boolean kickoutAfter = false;
private int maxSession = 1;
private Cache<String, Deque<Session>> cache;
public void setKickoutUrl(String kickoutUrl) {
this.kickoutUrl = kickoutUrl;
} public void setKickoutAfter(boolean kickoutAfter) {
this.kickoutAfter = kickoutAfter;
} public void setMaxSession(int maxSession) {
this.maxSession = maxSession;
} public void setCacheManager(CacheManager cacheManager) {
this.cache = cacheManager.getCache("shiro-kickout-session");
} @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return false;
} @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if(!"true".equals(kickout)){
//如果不需要单用户登录的限制
return true;
} Subject subject = getSubject(request, response);
if(!subject.isAuthenticated() && !subject.isRemembered()){
//如果没登录,直接进行之后的流程
return true;
} Session session = subject.getSession();
Serializable sessionId = session.getId(); String usernameTenant = (String)session.getAttribute("loginName");
synchronized (this.cache) {
if(cache == null){
throw new Exception("cache 为空");
}
Deque<Session> deque = cache.get(usernameTenant);
if (deque == null) {
deque = new LinkedList<Session>();
cache.put(usernameTenant, deque);
} //如果队列里没有此sessionId,且用户没有被踢出;放入队列
boolean whetherPutDeQue = true;
if (deque.isEmpty()) {
whetherPutDeQue = true;
} else {
for (Session sessionInqueue : deque) {
if (sessionId.equals(sessionInqueue.getId())) {
whetherPutDeQue = false;
break;
}
}
}
if (whetherPutDeQue) {
deque.push(session);
}
this.LOGGER.debug("logged user:" + usernameTenant + ", deque size = " + deque.size());
this.LOGGER.debug("deque = " + deque); //如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Session kickoutSession = null;
if (kickoutAfter) { //如果踢出后者
kickoutSession = deque.removeFirst();
this.LOGGER.debug("踢出后登录的,被踢出的sessionId为: " + kickoutSession.getId());
} else { //否则踢出前者
kickoutSession = deque.removeLast();
this.LOGGER.debug("踢出先登录的,被踢出的sessionId为: " + kickoutSession.getId());
}
if (kickoutSession != null) {
kickoutSession.stop();
}
}
}
return true;
}
}
3.遇到的错误和说明
3.1 共享session的问题
项目中,使用了共享session,出现了踢出失效的问题。(已解决)
解决办法:原本的实现代码使用的是标记属性,现在改为直接stop该session。
之前的代码:
if (kickoutSession != null) {
//设置会话的kickout属性表示踢出了
kickoutSession.setAttribute(KICK_OUT, true);
}
之后的代码:
if (kickoutSession != null) {
kickoutSession.stop();
}
2017.6.30 用shiro实现并发登录人数控制(实际项目中的实现)的更多相关文章
- 2017.4.12 开涛shiro教程-第十八章-并发登录人数控制
原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第十八章-并发登录人数控制 shiro中没有提 ...
- 第十八章 并发登录人数控制——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录:要么踢出前者登录(强制退出).比如spring securi ...
- SpringBoot 并发登录人数控制
通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能 dem ...
- 并发登录人数控制--Shiro系列(二)
为了安全起见,同一个账号理应同时只能在一台设备上登录,后面登录的踢出前面登录的.用Shiro可以轻松实现此功能. shiro中sessionManager是专门作会话管理的,而sessinManage ...
- visual studio 2017 创建 android 本地共享库(.so) 并从 C# android 项目中调用
Developing Xamarin Android Native Applications ★★★★★ ★★★★ ★★★ ★★ ★ February 23, 2015 by Ankit Asthan ...
- spring 整合shiro框架 模拟登录控制器。
一.导入shiro jar包. 我在maven项目中,将常用的jar包都放在里面. <?xml version="1.0" encoding="UTF-8&qu ...
- spring boot 2.0.0 + shiro + redis实现前后端分离的项目
简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大 ...
- SpringMVC+Apache Shiro+JPA(hibernate)案例教学(二)基于SpringMVC+Shiro的用户登录权限验证
序: 在上一篇中,咱们已经对于项目已经做了基本的配置,这一篇文章开始学习Shiro如何对登录进行验证. 教学: 一.Shiro配置的简要说明. 有心人可能注意到了,在上一章的applicationCo ...
- Windows登录脚本可以限制并发登录吗
在Windows服务器中,使用一个Windows登录脚本来限制并发会话靠谱吗? 事实上,这种解决方案存在很多缺点和弱点,并不能满足大中型IT基础设施的安全性需求. 一.使用登陆脚本限制并发会话,恶意用 ...
随机推荐
- webpack watch模式产生*.hot-update.json文件
webpack --watch会产生*.hot-update.json文件,解决方法如下: output: { path: path.join(root, "dist"), fil ...
- jQuery 鼠标滚轮事件
使用插件 jquery-mousewheel 下载 $('body').mousewheel(function(event, delta) { ? 'Up' : 'Down'; if (dir == ...
- Educational Codeforces Round 2 A. Extract Numbers
打开题目链接 题意:输入一个字符串,用,或:分隔输出字符串和整数(不含前导0和浮点数) ACcode: #include <iostream> #include <cstdio> ...
- 从日升的mecha anime看mecha genre的衰退
注:矢立肇是日升企画部集体笔名,如勇者系列便是公司企画 这里列出一些我看过认为有意思的动画,大抵同系列的就合并了,除非后续作品(剧场版,OVA,etc)并非直接剧情承接且有趣 注意我对长篇TV动画评价 ...
- java 符号引用与直接引用
简单来说: 符号引用就是字符串,这个字符串包含足够的信息,以供实际使用时可以找到相应的位置.你比如说某个方法的符号引用,如:“java/io/PrintStream.println:(Ljava/la ...
- [网络流24题] COGS 运输问题1
11. 运输问题1 ★★☆ 输入文件:maxflowa.in 输出文件:maxflowa.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 一个工厂每天生 ...
- [HNOI2002] 公交车路线
题目背景 在长沙城新建的环城公路上一共有8个公交站,分别为A.B.C.D.E.F.G.H.公共汽车只能够在相邻的两个公交站之间运行,因此你从某一个公交站到另外一个公交站往往要换几次车,例如从公交站A到 ...
- windows7下检测耳机麦克拔插(转)
原文转自 https://blog.csdn.net/rankun1/article/details/50972990 #include "stdafx.h" #define SA ...
- tcpreplay 缓存算法研究
一. 缓存算法 1.1 算法目的 流量拆分算法的运算会明显影响包的发送速率,为了提高发送速率, tcpreplay 使用了缓存机制,该部分代码也封装在tcpprep工具里,运行 tcpprep ( ...
- 自定义topo文件解析
from mininet.topo import Topo from mininet.net import Mininet from mininet.util import dumpNodeConne ...