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基础设施的安全性需求. 一.使用登陆脚本限制并发会话,恶意用 ...
随机推荐
- HTML,CSS,font-family:中文字体的英文名称【转载】
转自:http://www.9958.pw/post/html_font-family 宋体 SimSun 黑体 SimHei 微软雅黑 Microsoft YaHei 微软正黑体 Microsoft ...
- Pandas之DataFrame——Part 3
''' [课程2.] 数值计算和统计基础 常用数学.统计方法 ''' # 基本参数:axis.skipna import numpy as np import pandas as pd df = pd ...
- RSA加密/解密 Decryption error异常解决
RSA加密/解密 Decryption error异常解决 import java.io.ByteArrayOutputStream; import java.security.Key; import ...
- hihoCoder 1015KMP
#include <iostream> #include <algorithm> #include <stdio.h> #include <math.h> ...
- linux--lsof
lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件.所以如传输控制协议 ...
- 51nod 1273 旅行计划——思维题
某个国家有N个城市,编号0 至 N-1,他们之间用N - 1条道路连接,道路是双向行驶的,沿着道路你可以到达任何一个城市.你有一个旅行计划,这个计划是从编号K的城市出发,每天到达一个你没有去过的城市, ...
- Android APK瘦身方法小结
众所周知,APP包体的大小,会影响推广的难度,用户不太喜欢下载太大的APP,同类型同等功能的APP中往往是包体小的更受用户的青睐,所以降低包体是一项非常必要的事情,也是最近公司的APP需要降低包体,所 ...
- VSCode配置c++环境简单教程
VSCode配置c++环境简单教程 1.下载MinGW 安装有关gdb,gcc,g++的所有包 2.文件夹 打开一个文件夹 在里面随便写一个cpp 不管是VS还是VSCode,它的基本操作单位都是文件 ...
- centos7刚安装需要的一些基础优化
基本操作一:更改主机名 centos7有一个新的修改主机名的命令hostnamectl # hostnamectl set-hostname --static benjamin # vim /etc/ ...
- Switch能否用string做参数
在Java5以前,switch(expr)中,exper只能是byte,short,char,int类型(或其包装类)的常量表达式. 从Java5开始,java中引入了枚举类型,即enum类型. 从J ...