`
jinnianshilongnian
  • 浏览: 21518092 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2421267
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3011045
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5640984
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:260332
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1598237
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250437
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5861250
Group-logo
跟我学Nginx+Lua开...
浏览量:703156
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:786050
社区版块
存档分类
最新评论

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

阅读更多

 

目录贴: 跟我学Shiro目录贴

 

在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录;要么踢出前者登录(强制退出)。比如spring security就直接提供了相应的功能;Shiro的话没有提供默认实现,不过可以很容易的在Shiro中加入这个功能。

 

示例代码基于《第十六章 综合实例》完成,通过Shiro Filter机制扩展KickoutSessionControlFilter完成。

 

首先来看看如何配置使用(spring-config-shiro.xml

  

kickoutSessionControlFilter用于控制并发登录人数的 

<bean id="kickoutSessionControlFilter" 
class="com.github.zhangkaitao.shiro.chapter18.web.shiro.filter.KickoutSessionControlFilter">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="sessionManager" ref="sessionManager"/>

    <property name="kickoutAfter" value="false"/>
    <property name="maxSession" value="2"/>
    <property name="kickoutUrl" value="/login?kickout=1"/>
</bean> 

cacheManager:使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;

sessionManager:用于根据会话ID,获取会话进行踢出操作的;

kickoutAfter:是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;

maxSession:同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;

kickoutUrl:被踢出后重定向到的地址;

 

shiroFilter配置 

   <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
                <entry key="sysUser" value-ref="sysUserFilter"/>
                <entry key="kickout" value-ref="kickoutSessionControlFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /login = authc
                /logout = logout
                /authenticated = authc
                /** = kickout,user,sysUser
            </value>
        </property>
    </bean> 

此处配置除了登录等之外的地址都走kickout拦截器进行并发登录控制。

 

测试

此处因为maxSession=2,所以需要打开3个浏览器(需要不同的浏览器,如IEChromeFirefox),分别访问http://localhost:8080/chapter18/进行登录;然后刷新第一次打开的浏览器,将会被强制退出,如显示下图: 


KickoutSessionControlFilter核心代码: 

protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    Subject subject = getSubject(request, response);
    if(!subject.isAuthenticated() && !subject.isRemembered()) {
        //如果没有登录,直接进行之后的流程
        return true;
    }

    Session session = subject.getSession();
    String username = (String) subject.getPrincipal();
    Serializable sessionId = session.getId();

    //TODO 同步控制
    Deque<Serializable> deque = cache.get(username);
    if(deque == null) {
        deque = new LinkedList<Serializable>();
        cache.put(username, deque);
    }

    //如果队列里没有此sessionId,且用户没有被踢出;放入队列
    if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
        deque.push(sessionId);
    }

    //如果队列里的sessionId数超出最大会话数,开始踢人
    while(deque.size() > maxSession) {
        Serializable kickoutSessionId = null;
        if(kickoutAfter) { //如果踢出后者
            kickoutSessionId = deque.removeFirst();
        } else { //否则踢出前者
            kickoutSessionId = deque.removeLast();
        }
        try {
            Session kickoutSession =
                sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
            if(kickoutSession != null) {
                //设置会话的kickout属性表示踢出了
                kickoutSession.setAttribute("kickout", true);
            }
        } catch (Exception e) {//ignore exception
        }
    }

    //如果被踢出了,直接退出,重定向到踢出后的地址
    if (session.getAttribute("kickout") != null) {
        //会话被踢出了
        try {
            subject.logout();
        } catch (Exception e) { //ignore
        }
        saveRequest(request);
        WebUtils.issueRedirect(request, response, kickoutUrl);
        return false;
    }
    return true;
} 

此处使用了Cache缓存用户名—会话id之间的关系;如果量比较大可以考虑如持久化到数据库/其他带持久化的Cache中;另外此处没有并发控制的同步实现,可以考虑根据用户名获取锁来控制,减少锁的粒度。

 

另外可参考JavaEE项目开发脚手架,其提供了后台踢出用户的功能:

https://github.com/zhangkaitao/es/blob/master/web/src/main/java/com/sishuok/es/sys/user/web/controller/UserOnlineController.java 

    

 

 

示例源代码:https://github.com/zhangkaitao/shiro-example;可加群 231889722 探讨Spring/Shiro技术。

        

  

13
1
分享到:
评论
4 楼 zqb666kkk 2014-04-02  
但是有一个问题


必须是另一个帐号登录后并且做一次操作


前一个帐号才会退出


而不是后一个帐号登录后 前一个帐号做操作的时候才退出,必须后一个帐号登录后 做一次操作,进入kickout拦截器 进行踢出操作  后 前一个帐号才能够在再一次操作的时候被强制退出

一般情况 都是 后一个帐号只要一登录 前一个帐号做操作的时候就会强制退出
请问 这个情况怎么解决好?
3 楼 Long_yuan 2014-04-02  
Long_yuan 写道
URL 方法 结果 类型 已接收 已花费 发起程序 等候‎‎ 开始‎‎ 请求‎‎ 响应‎‎ 已读取缓存‎‎ 差距‎‎
http://localhost:8080/doc/login;JSESSIONID=a3571ee3-0228-4cc6-9188-0dbd90d57533?kickout=1 GET 200 text/html .62 KB 31 ms 导航 0 0 31 0 0 86


怎么避免它自动加上JSESSIONID 呢?




找到了你的博客 解决了 http://jinnianshilongnian.iteye.com/blog/1831408
2 楼 Long_yuan 2014-04-02  
URL 方法 结果 类型 已接收 已花费 发起程序 等候‎‎ 开始‎‎ 请求‎‎ 响应‎‎ 已读取缓存‎‎ 差距‎‎
http://localhost:8080/doc/login;JSESSIONID=a3571ee3-0228-4cc6-9188-0dbd90d57533?kickout=1 GET 200 text/html .62 KB 31 ms 导航 0 0 31 0 0 86


怎么避免它自动加上JSESSIONID 呢?
1 楼 lican 2014-04-02  
想请教一下,集群环境下又如何控制呢?

相关推荐

    第十七章 OAuth2集成——《跟我学Shiro》 - 开涛的博客 - ITeye技术网站2

    在《跟我学Shiro》的第十七章中,作者开涛介绍了如何集成OAuth2,使用Apache Oltu作为OAuth2服务端的实现。实现中涉及以下关键部分: 1. **依赖**:引入了`authzserver`(授权服务器依赖)和`resourceserver`(资源...

    跟我一起学shiro 张开涛

    《跟我一起学Shiro——张开涛》这本书是针对初学者的优秀教程,旨在帮助读者快速理解和掌握Shiro的基本用法和核心概念。 **1. Shiro基础** Shiro的基础概念包括Subject、Realms、Cryptography和Session。Subject是...

    [资料][Java]跟我学Shiro教程_Java跟我学Shiro教程_shiro_

    Apache Shiro 是一个强大且易用的 Java 安全框架,提供了认证、授权、加密和会话管理功能,可以非常方便地开发出足够安全的应用。...阅读 "[资料][Java]跟我学Shiro教程.pdf",你将得到更详细的步骤指导和实践案例。

    跟我学Shiro

    通过《跟我学Shiro》.pdf,你将学习到如何创建 Realm 实现数据源连接、配置 Shiro 安全框架、处理登录和登出逻辑、实现权限控制以及在实际项目中部署和调优 Shiro。 10. **最佳实践** 学习 Shiro 的过程中,了解...

    跟我学shiro

    ### Apache Shiro 知识点概览 #### 一、Apache Shiro 框架...#### 十八、并发登录人数控制 - **概念**:限制同时在线的用户数量,以防止资源被过度消耗。 - **实现**:通过 Shiro 的配置和自定义实现来达到控制目的。

    《跟我学Shiro》- 张开涛.txt

    ***txt文件中含有下载地址** 《跟我学Shiro》- 张开涛,PDF版本,带目录,清晰。 示例源代码:https://github.com/zhangkaitao/shiro-example; 加qun 231889722 探讨Spring/Shiro技术。

    跟我学Shiro教程.rar

    Apache Shiro是一个强大的Java安全框架,它为应用程序提供了身份验证(Authentication)、授权...通过阅读"跟我学Shiro教程.pdf",你应该能够了解如何将Shiro集成到你的项目中,以及如何利用它来实现安全控制。

    跟我学Shiro教程及其课程分章节源码

    Apache Shiro是一个强大易用的Java安全框架,...我找了一版 跟我学Shiro教程PDF,里面讲的很详细.里面还附带了每个章节的源码.值得你收藏哟!饮水思源——原文出自:http://jinnianshilongnian.iteye.com/blog/2049092

    跟我学Shiro-java开发+spring开发

    《跟我学Shiro-java开发+spring开发》是一个深入学习Java安全框架Shiro和Spring集成的教程,旨在帮助开发者掌握这两个关键技术在实际项目中的应用。Shiro是一个强大的且易用的Java安全框架,提供了认证、授权、加密...

    跟我学Shiro教程 pdf

    《跟我学Shiro》PDF完结版下载, Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不...

    shiro教程 跟我学Shiro教程

    "跟我学Shiro教程"资源包包含了全面学习Shiro所需的重要材料,包括文档和实践示例。 首先,我们来看《Apache_Shiro参考手册中文版.pdf》。这本书籍详细介绍了Shiro框架的各个组件和使用方法。通过阅读,你可以了解...

    跟我学Shiro第11章Demo

    在"跟我学Shiro第11章Demo"中,我们将深入探讨Shiro的核心组件,特别是其在缓存管理和会话管理中的应用。 首先,我们关注的是Cache缓存。Shiro支持缓存来提高性能,避免频繁的数据库查询。它允许开发者将敏感操作的...

    跟我学 Shiro - v1.1.rar

    Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Sp ring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所 ...

    跟我学shiro源代码

    "跟我学Shiro源代码"是一份针对Shiro框架的详细教程,通过这本书,读者能够深入理解Shiro的核心概念和用法,并通过实际的代码示例来提升自己的技能。 1. **身份验证(Authentication)**:Shiro提供了一套完善的...

    跟我学Shiro第13章Demo(RememberMe)

    "跟我学Shiro第13章Demo(RememberMe)"是一个实战教程,旨在帮助开发者理解并实现Shiro中的RememberMe特性。RememberMe功能允许用户在一段时间内免于重新登录,提高了用户体验。 在这个Demo中,我们将探讨以下几个...

    跟我学shiro文档及源码

    在《跟我学Shiro》这本书中,作者深入浅出地讲解了 Shiro 的核心概念与实际应用场景,配合源码分析,有助于读者更好地理解和掌握 Shiro 的工作原理。 1. **Shiro 概述** - Shiro 的设计目标是简化应用安全开发,它...

Global site tag (gtag.js) - Google Analytics