之前的学习总结:http://www.cnblogs.com/lyh421/p/6698871.html

1.kickout功能描述

如果将配置文件中的kickout设置为true,则在另处再次登录时,会将第一次登录的用户踢出。
 

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实现并发登录人数控制(实际项目中的实现)的更多相关文章

  1. 2017.4.12 开涛shiro教程-第十八章-并发登录人数控制

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第十八章-并发登录人数控制 shiro中没有提 ...

  2. 第十八章 并发登录人数控制——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录:要么踢出前者登录(强制退出).比如spring securi ...

  3. SpringBoot 并发登录人数控制

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

  4. 并发登录人数控制--Shiro系列(二)

    为了安全起见,同一个账号理应同时只能在一台设备上登录,后面登录的踢出前面登录的.用Shiro可以轻松实现此功能. shiro中sessionManager是专门作会话管理的,而sessinManage ...

  5. visual studio 2017 创建 android 本地共享库(.so) 并从 C# android 项目中调用

    Developing Xamarin Android Native Applications ★★★★★ ★★★★ ★★★ ★★ ★ February 23, 2015 by Ankit Asthan ...

  6. spring 整合shiro框架 模拟登录控制器。

    一.导入shiro  jar包.  我在maven项目中,将常用的jar包都放在里面. <?xml version="1.0" encoding="UTF-8&qu ...

  7. spring boot 2.0.0 + shiro + redis实现前后端分离的项目

    简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大 ...

  8. SpringMVC+Apache Shiro+JPA(hibernate)案例教学(二)基于SpringMVC+Shiro的用户登录权限验证

    序: 在上一篇中,咱们已经对于项目已经做了基本的配置,这一篇文章开始学习Shiro如何对登录进行验证. 教学: 一.Shiro配置的简要说明. 有心人可能注意到了,在上一章的applicationCo ...

  9. Windows登录脚本可以限制并发登录吗

    在Windows服务器中,使用一个Windows登录脚本来限制并发会话靠谱吗? 事实上,这种解决方案存在很多缺点和弱点,并不能满足大中型IT基础设施的安全性需求. 一.使用登陆脚本限制并发会话,恶意用 ...

随机推荐

  1. linux下有的目录有可执行权限

    在linux里面,目录也是一个文件.权限设置方法与普通文件一样.目录的可执行权限:目录无所谓执行不执行,目录只能否被开启(也称作“遍历”或“搜索”)对目录的执行权限不仅意味着查看目录下文件名还允许查看 ...

  2. [bzoj2301][HAOI2011]Problem B —— 莫比乌斯反演+容斥原理

    题意 给定a, b, c, d, k,求出: \[\sum_{i=a}^b\sum_{j=c}^d[gcd(i, j) = k]\] 题解 为方便表述,我们设 \[calc(\alpha, \beta ...

  3. [bzoj2190][SDOI2008]仪仗队 ——欧拉函数

    题解 以c点为(0, 0)建立坐标系,可以发现, 当(x,y)!=1,即x,y不互素时,(x,y)点一定会被点(x/n, y/n)遮挡. 所以点(x, y)被看到的充分必要条件是Gcd(x, y) = ...

  4. oracle odbc 驱动安装(不安装oracle客户端)

    1.下载odbc驱动 需要下载两个东西 instantclient-basiclite-nt-12.1.0.1.0.zip instantclient-odbc-nt-12.1.0.1.0.zip 由 ...

  5. Scenes in Cocos2D

    https://www.makeschool.com/docs/?source=mgwu#!/cocos2d/1.0/scenes Scenes in Cocos2D In Cocos2D you h ...

  6. 【原创】Linux环境下的图形系统和AMD R600显卡编程(6)——AMD显卡GPU命令格式

    前面一篇blog里面描述了命令环缓冲区机制,在命令环机制下,驱动写入PM4(不知道为何会取这样一个名字)包格式的命令对显卡进行配置.这一篇blog将详细介绍命令包的格式. 当前定义了4中命令包,分别是 ...

  7. 信号槽库:sigslot.h和sigc++使用

    用qt的知道,qt有方便简单的信号槽机制,但需要专门的qt工具处理. 如果想直接使信号槽就可以使用sigslot库,或者sigc++库,或者boost中的signals,这里介绍sigslot和sig ...

  8. java keytool证书工具使用小结(转)

    Keytool 是一个Java数据证书的管理工具 ,Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里,包含两种数据:密钥实体(K ...

  9. Python之jieba分词

    jieba,很有意思的一个模块,专门用来分词. import jieba # sentence:分割的中文字符串 # cut_all:是否采用全模式,默认为False表示精确模式 # HMM:表示是否 ...

  10. Python 如何调用 Java

    引用了这个文章,请打开链接  http://www.cnblogs.com/junrong624/p/5278457.html 日后待补写 ...