Shiro 限制并发登录人数案例

JAVA herman 2827浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

spring security 中存在这样的功能:每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录;要么踢出前者登录(强制退出)。Shiro的话没有提供默认实现,不过可以很容易的在Shiro中加入这个功能。

kickoutSessionControlFilter 控制并发登录人数

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中;另外此处没有并发控制的同步实现,可以考虑根据用户名获取锁来控制,减少锁的粒度。

spring-config-shiro.xml 配置

配置内容如下:

<bean id="kickoutSessionControlFilter"   
class="com.xttblog.shiro17.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:被踢出后重定向到的地址;

ShiroFilterFactoryBean 配置

<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个浏览器(需要不同的浏览器,如IE、Chrome、Firefox),分别访问http://localhost:8080/shiro17/进行登录;然后刷新第一次打开的浏览器,将会被强制退出,如显示下图: 

Shiro 限制同时登录人数案例

本文源码下载链接:http://pan.baidu.com/s/1cEMyUq 密码:oda2

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » Shiro 限制并发登录人数案例