Cas实现子系统登录互踢

版权声明:转载请附链接 https://blog.csdn.net/qq_23536449/article/details/89916049

实现思路

1.一个类,类中维护着一个已登录用户的map,每次登录取出上次该用户对应的session;并给session添加一个过期的属性标识lastHttpSession.setAttribute(BE_KICKED,"您的账号在另一地点登录,您被迫下线")

2.写一个sessionFilter,该sessionFilter取出每个request请求实例中维护的session,判断session中有没有BE_KICKEd

3.如果存在说明该session是被踢掉用户,返回给客户端被踢信息

废话不多说直接上代码

UserStatistiUtil 

package com.gysoft.sso.utils;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import javax.servlet.http.HttpSession;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author 周宁
 * @Date 2018-06-13 20:14
 */
public class UserStatisticUtil {
    /**
     * 默认缓存2个小时
     */
    public static final Integer DEFAULT_CACHE_SECONDS = 60*60*2;
    /**
     * 缓存容器的默认大小
     */
    private static final Integer DEFAULT_SIZE = 10000;

    /**
     * 存放用户对应Session的map,用于踢出用户
     */
    public static ConcurrentHashMap<String,HttpSession> userSessionMap = new ConcurrentHashMap();

    /**
     * 缓存容器,存放被踢下线的SessionId,如果一个被踢用户过了2小时去请求那么就是session超时而不是被踢
     */
    private static final Cache<String, HttpSession> invalidSessionIdMap = CacheBuilder.newBuilder().maximumSize(DEFAULT_SIZE)
            .expireAfterWrite(DEFAULT_CACHE_SECONDS, TimeUnit.SECONDS).build();
    /**
     * 判断是否是过期的sessionId
     * @param sessionId
     * @return
     */
    public static boolean isInvalidSessionId(String sessionId){
        return getInvalidSessionId(sessionId)!=null;
    }
    /**
     * 取出已经过期的sessionId
     * @param key
     * @return Object
     */
    public static HttpSession getInvalidSessionId(String key) {
        return invalidSessionIdMap.getIfPresent(key);
    }

    /**
     * 放入已经过期的sessionId
     * @param key
     * @param value
     */
    public static void putInvalidSessionId(String key, HttpSession value) {
        invalidSessionIdMap.put(key, value);
    }

}

CustomCas30ProxyReceivingTicketValidationFilter

public class CustomCas30ProxyReceivingTicketValidationFilter extends Cas30ProxyReceivingTicketValidationFilter {
    @Override
    protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response, Assertion assertion) {
        String username =assertion.getPrincipal().getName();
        HttpSession lastHttpSession = UserStatisticUtil.userSessionMap.get(username);
        if(null!=lastHttpSession&&!UserStatisticUtil.isInvalidSessionId(lastHttpSession.getId())){
            try{
                lastHttpSession.setAttribute(BE_KICKED,"您的账号在另一地点登录,您被迫下线");
            }catch (IllegalStateException e){
                //TODO Nothing 出现这种情况的原因是浏览器的session过期了但是未被及时清除
            }
        }
        String dcpLoginInfo = (String) assertion.getPrincipal().getAttributes().get(DCP_LOGIN_INFO);
        HttpSession session = request.getSession();
        session.setAttribute(DCP_LOGIN_INFO,dcpLoginInfo);
        UserStatisticUtil.userSessionMap.put(username,session);
    }
}

SessionTimeOutFilter

package com.gysoft.sso.filter;


import com.gysoft.sso.utils.UserStatisticUtil;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

import static com.gysoft.sso.bean.SessionVariable.BE_KICKED;
import static com.gysoft.sso.bean.SessionVariable.DCP_LOGIN_INFO;
import static com.gysoft.sso.utils.UserStatisticUtil.DEFAULT_CACHE_SECONDS;

/**
 * session超时过滤
 * @author 周宁
 * @Date 2018-06-12 14:08
 */
public class SessionTimeOutFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        String queryString = req.getQueryString();
        //url地址需要单点认证,且session不存在,则需要验证地址是否包含ticket
        if(!CustomAuthenticationFilter.isRequestUrlExcluded(req)&&session.getAttribute(DCP_LOGIN_INFO)==null){
            if(queryString==null||queryString.indexOf("ticket")==-1){
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().write("{\"msg\": \"用户未登录\",\"code\": " + 302 + ",\"result\":  "+null+"}");
                return;
            }
        }
        //在线互踢
        if(session.getAttribute(BE_KICKED)!=null){
            UserStatisticUtil.putInvalidSessionId(session.getId(),session,DEFAULT_CACHE_SECONDS);
            String msg = (String) session.getAttribute(BE_KICKED);
            session.invalidate();
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("{\"msg\": "+msg+",\"code\": " + 302 + ",\"result\": " + null + "}");
            return;
        }
        chain.doFilter(req,res);
    }

    @Override
    public void destroy() {

    }
}

猜你喜欢

转载自blog.csdn.net/qq_23536449/article/details/89916049