之前的学习总结: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. MFC 加载资源文件里的png

    static bool LoadImageFromResource(IN CImage* pImage, IN UINT nResID, IN LPCWSTR lpTyp) { if ( pImage ...

  2. 7月15号day7总结

    今天复习了springMVC的框架搭建. 思维导图:

  3. 汕头市队赛 SRM1X T1

    木之本樱 背景 “西瓜是可以种在树上的!”——木之本樱 描述 空地上,一排排的西瓜树拔地而起. 魔法世界里,空地是无限大的.所有的树排成了n条直线,每条直线也是向左右两端无限延伸的. 由于自己姓木(之 ...

  4. Selenium2设置隐式等待和显示等待

    1. 设置显示等待 Java代码: public static WebElement WaitForElement(WebDriver driver, String locator) { WebEle ...

  5. Uva 10158 War

    并查集的应用 直接阔成2倍.后N项为对应的敌人 #include <map> #include <set> #include <list> #include < ...

  6. poj 2546(两圆公共面积)

    Circular Area Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5682   Accepted: 2225 Des ...

  7. ***七牛跨域上传图片JS SDK

    SDK: http://developer.qiniu.com/code/v6/sdk/javascript.html#upload 上传 在页面中引入 plupload,plupload.full. ...

  8. J.U.C并发框架源码阅读(三)ReentrantLock

    基于版本jdk1.7.0_80 java.util.concurrent.locks.ReentrantLock 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. ...

  9. SpringBoot动态数据源

    1.原理图 2.创建枚举类 /** * 存数据源key值 */ public enum DataSourceKey { master,salve,migration } 3.创建自定义注解类 /** ...

  10. bzoj2440(莫比乌斯函数)

    bzoj2440 题意 求第 k 个不是完全平方数(除 1 以外)的正倍数的数. 分析 利用二分法求解,二分 x ,判断 x 是否是第 k 个数即可,那么我们就要计算 [1, x] 有几个符合条件的数 ...