之前的学习总结: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. POJ2175:Evacuation Plan(消负圈)

    Evacuation Plan Time Limit: 1000MSMemory Limit: 65536KTotal Submissions: 5665Accepted: 1481Special J ...

  2. Pandas之DataFrame——Part 2

    ''' [课程2.] 时间模块:datetime datetime模块,主要掌握:datetime.date(), datetime.datetime(), datetime.timedelta() ...

  3. 用VS2010编写的C++程序,在其他电脑上无法运行的问题

    问题:在自己电脑上用VS2010编写的VC++程序(使用MFC库),不能在其他电脑上运行.双击提示: “无法启动此程序,因为计算机中丢失mfc100u.dll 尝试重新安装该程序以解决此问题. 解决方 ...

  4. JAVA中GC时finalize()方法是不是一定会被执行?

    在回答上面问题之前,我们一定要了解JVM在进行垃圾回收时的机制,首先: 一.可达性算法  要知道对象什么时候死亡,我们需要先知道JVM的GC是如何判断对象是可以回收的.JAVA是通过可达性算法来来判断 ...

  5. OpenCV 2.4.9 学习笔记(2)—— OpenCV内存自动管理

    OpenCV自动内存管理 目前版本的OpenCV是自动处理所有自己的内存的,虽然这么说也不是很严谨.OpenCV在2.0版本中引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法.使用这个 ...

  6. scrapy爬取段子

    scrapy.py 1.cmd运行scrapy shell http://www.baidu.com response.xpath('//div[@aa="bb"]') 找到需要匹 ...

  7. Autotools Mythbuster

    Preface Diego Elio "Flameeyes" Pettenò Author and Publisher <flameeyes@flameeyes.eu> ...

  8. 原 android重启应用(应用重新启动自身)

    private void restartApplication() { final Intent intent = getPackageManager().getLaunchIntentForPack ...

  9. 【Android开发日记】之入门篇(三)——Android目录结构

    本来的话,这一章想要介绍的是Android的系统架构,毕竟有了这些知识的储备,再去看实际的项目时才会更清楚地理解为什么要这样设计,同时在开发中遇到难题,也可以凭借着对Android的了解,尽快找出哪些 ...

  10. Fiddler抓包6-get请求(url详解)【转载】

    本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/fiddler/ 前言 上一篇介绍了Composer的功能,可以模拟get和post请求 ...