• Shiro和Spring 集合实现同一个账号只能一个人在线使用,其它人在使用进行剔除(八)


    1、实现原理其实就是自定义过滤器,然后登录时,A登录系统后,B也登录了,这个时候获取此账号之前的session给删除,然后将新的session放入到缓存里面去,一个账户对应一个有序的集合

    编写自定义过滤器:KickoutSessionControlFilter.java

      1 import java.io.Serializable;
      2 import java.util.Deque;
      3 import java.util.LinkedList;
      4 
      5 import javax.servlet.ServletRequest;
      6 import javax.servlet.ServletResponse;
      7 import javax.servlet.http.HttpServletRequest;
      8 import javax.servlet.http.HttpServletResponse;
      9 
     10 import org.apache.shiro.cache.Cache;
     11 import org.apache.shiro.cache.CacheManager;
     12 import org.apache.shiro.session.Session;
     13 import org.apache.shiro.session.mgt.DefaultSessionKey;
     14 import org.apache.shiro.session.mgt.SessionManager;
     15 import org.apache.shiro.subject.Subject;
     16 import org.apache.shiro.web.filter.AccessControlFilter;
     17 import org.apache.shiro.web.util.WebUtils;
     18 
     19 import com.itzixi.pojo.ActiveUser;
     20 
     21 /**
     22  * 
     23  * @Title: KickoutSessionControlFilter.java
     24  * @Description: 同一用户后登陆踢出前面的用户
     25  * @date 2016年12月12日 下午7:25:40
     26  * @version V1.0
     27  */
     28 public class KickoutSessionControlFilter extends AccessControlFilter {
     29 
     30     private String kickoutUrl; //踢出后到的地址
     31     private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
     32     private int maxSession = 1; //同一个帐号最大会话数 默认1
     33 
     34     private SessionManager sessionManager;
     35     
     36     // TODO 分布式集群环境下,需要改为redis
     37     private Cache<String, Deque<Serializable>> cache;
     38 
     39     public void setKickoutUrl(String kickoutUrl) {
     40         this.kickoutUrl = kickoutUrl;
     41     }
     42 
     43     public void setKickoutAfter(boolean kickoutAfter) {
     44         this.kickoutAfter = kickoutAfter;
     45     }
     46 
     47     public void setMaxSession(int maxSession) {
     48         this.maxSession = maxSession;
     49     }
     50 
     51     public void setSessionManager(SessionManager sessionManager) {
     52         this.sessionManager = sessionManager;
     53     }
     54 
     55     public void setCacheManager(CacheManager cacheManager) {
     56         this.cache = cacheManager.getCache("shiro-kickout-session");
     57     }
     58 
     59     @Override
     60     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
     61         return false;
     62     }
     63 
     64     @Override
     65     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
     66         Subject subject = getSubject(request, response);
     67         if(!subject.isAuthenticated() && !subject.isRemembered()) {
     68             //如果没有登录,直接进行之后的流程
     69             return true;
     70         }
     71 
     72         Session session = subject.getSession();
     73         ActiveUser user = (ActiveUser)subject.getPrincipal();
     74         String username = user.getUsername();
     75         Serializable sessionId = session.getId();
     76 
     77         // 同步控制, 同步在本机的缓存中是有效的,但是一旦放入集群中,就会失效
     78         Deque<Serializable> deque = cache.get(username);
     79         if(deque == null) {
     80             deque = new LinkedList<Serializable>();
     81             cache.put(username, deque);
     82         }
     83 
     84         //如果队列里没有此sessionId,且用户没有被踢出;放入队列
     85         if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
     86             deque.push(sessionId);
     87         }
     88 
     89         //如果队列里的sessionId数超出最大会话数,开始踢人
     90         while(deque.size() > maxSession) {
     91             Serializable kickoutSessionId = null;
     92             if(kickoutAfter) { //如果踢出后者
     93                 kickoutSessionId = deque.removeFirst();
     94             } else { //否则踢出前者
     95                 kickoutSessionId = deque.removeLast();
     96             }
     97             try {
     98                 Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
     99                 if(kickoutSession != null) {
    100                     //设置会话的kickout属性表示踢出了
    101                     kickoutSession.setAttribute("kickout", true);
    102                 }
    103             } catch (Exception e) {//ignore exception
    104             }
    105         }
    106 
    107         //如果被踢出了,直接退出,重定向到踢出后的地址
    108         if (session.getAttribute("kickout") != null) {
    109             //会话被踢出了
    110             try {
    111                 subject.logout();
    112             } catch (Exception e) { //ignore
    113             }
    114             saveRequest(request);
    115             
    116             HttpServletRequest httpRequest = WebUtils.toHttp(request);
    117             if (ShiroFilterUtils.isAjax(httpRequest)) {
    118                 HttpServletResponse httpServletResponse = WebUtils.toHttp(response);  
    119                 httpServletResponse.sendError(ShiroFilterUtils.HTTP_STATUS_SESSION_EXPIRE);
    120                 return false;
    121             } else {
    122                 WebUtils.issueRedirect(request, response, kickoutUrl);
    123                 return false;
    124             }
    125         }
    126 
    127         return true;
    128     }
    129 }

    2、在applicationContext-shiro.xml配置文件中增加如下配置: 

       注意:必须使用本机的ehcache缓存来存储,不能使用集群的redis缓存

     1 <!--自定义filter实现同一个账户只能同时一个人在线,后者登录的踢出前面登录的用户-->
     2     <bean id="kickoutSessionControlFilter" class="com.itzixi.web.shiro.filter.KickoutSessionControlFilter">
     3         <property name="cacheManager" ref="shiroEhcacheManager"/>
     4         <property name="sessionManager" ref="sessionManager"/>
     5         <!-- 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户 -->
     6         <property name="kickoutAfter" value="false"/>
     7         <!-- 同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录 -->
     8         <property name="maxSession" value="1"/>
     9         <property name="kickoutUrl" value="/login.action"/>
    10     </bean>

    3、修改shiro过滤器的主题配置:如下图红色的标注为 新增 或 修改的

  • 相关阅读:
    BZOJ2023: [Usaco2005 Nov]Ant Counting 数蚂蚁
    BZOJ2044: 三维导弹拦截
    BZOJ2982: combination
    Solidity(address的四个方法)
    Solidity基础
    如何搭建以太坊的私有链
    挖矿
    智能合约
    密码学
    比特币
  • 原文地址:https://www.cnblogs.com/yinfengjiujian/p/9087511.html
Copyright © 2020-2023  润新知