站点单用户登录,后面登录上来的会把前面登录的人踢下线

公司平台接受监管后,系统整改,其中一个就是一个用户账号只能在一个地方登录,别的地方登录上来后,当前登录的人必须下线。

因为一直做web开发,之前给某公司做过这个功能,只不过那个是8年前,比较老的方式,单体系统,目前都是分布式。但是其实实现原理都是一样的。分布式系统只是部署了多份app。那么就得依赖一个第三方存储的地方,可以是db,可以是缓存。这里我就用缓存redis。

首先说一下这个单账号只能一个ip登录的原理:

明天继续。先贴代码:

LoginAloneHttpSessionListener.java

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;


@Component
@WebListener
public class LoginAloneHttpSessionListener implements HttpSessionListener,  HttpSessionAttributeListener {
    
    private static Logger logger = Logger.getLogger(LoginAloneHttpSessionListener.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    /**初始化方法 stringRedisTemplate
     * 
     * @param session
     * @author chenweixian 陈惟鲜
     * @date 2018年2月26日 上午10:40:02
     */
    public void initStringRedisTemplate(HttpSession session){
        if (stringRedisTemplate == null){
            ServletContext servletContext = session.getServletContext();  
            ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
            stringRedisTemplate = context.getBean("stringRedisTemplate", StringRedisTemplate.class);
        }
    }

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        this.initStringRedisTemplate(event.getSession());
        
//        logger.info("============session【"+event.getSession().getId()+"】 已创建");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        this.initStringRedisTemplate(event.getSession());
        BoundHashOperations<String, String, String> boundHashOperations = stringRedisTemplate.boundHashOps(RedisContants.ADMIN_LOGIN_MAP);
        boundHashOperations.delete(event.getSession().getId());
        
//        logger.info("============session【"+event.getSession().getId()+"】 已销毁");
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {

        this.handleUserInfo(event);
        
//        logger.info("============session【"+event.getSession().getId()+"】====attribute  Added");
        
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
        if (SystemContants.USER_INFO.equals(event.getName())){
            this.initStringRedisTemplate(event.getSession());
            BoundHashOperations<String, String, String> boundHashOperations = stringRedisTemplate.boundHashOps(RedisContants.ADMIN_LOGIN_MAP);
            boundHashOperations.delete(event.getSession().getId());
        }
        
//        logger.info("============session【"+event.getSession().getId()+"】====attribute Removed");
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
        this.handleUserInfo(event);

//        logger.info("============session【"+event.getSession().getId()+"】=====attribute Replaced");
        
    }  
    
    /**处理用户信息
     * 
     * @param event
     * @author chenweixian 陈惟鲜
     * @date 2018年2月26日 上午11:07:11
     */
    private void handleUserInfo(HttpSessionBindingEvent event){
        if (SystemContants.USER_INFO.equals(event.getName())){
            this.initStringRedisTemplate(event.getSession());
            UserVo userVo = (UserVo)event.getValue();
            BoundHashOperations<String, String, String> boundHashOperations = stringRedisTemplate.boundHashOps(RedisContants.ADMIN_LOGIN_MAP);
            // 踢出之前在线的用户
            if (userVo != null){
                if (boundHashOperations.keys().size() > 0){
                    for (String key :  boundHashOperations.keys()){
                        if(userVo.getUserId().equalsIgnoreCase(boundHashOperations.get(key))){
                            // 踢出
                            boundHashOperations.delete(key);
                        }
                    }
                }
            }

            // 加入当前登录用户
            boundHashOperations.put(event.getSession().getId(), userVo.getUserId());
        }
    }
    
}

LoginFilter.java

import java.io.IOException;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.opensymphony.oscache.util.StringUtil;

/**登录过滤器
 * 
 * @author : chewneixian 陈惟鲜
 * @create_date 2016年8月3日 上午11:35:23
 *
 */
@Service
@WebFilter(filterName="loginFilter", urlPatterns={
        "*.do",
        })
public class LoginFilter implements Filter {
    @Autowired
    private DictionaryVoService dictionaryVoService;
    // 忽略的URL
//    String PASS_VALIDATION_URL = ConfigPropertiesUtil.getProp("login.ignore.passUrl");

    //     日志对象
    private static Logger logger = Logger.getLogger(LoginFilter.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext servletContext = filterConfig.getServletContext();  
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        if (stringRedisTemplate == null){
            stringRedisTemplate = context.getBean("stringRedisTemplate", StringRedisTemplate.class);
        }
        if (dictionaryVoService == null){
            dictionaryVoService = context.getBean("dictionaryVoService", DictionaryVoService.class);
        }
    }
    
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;        
        String PASS_VALIDATION_URL = dictionaryVoService.findFieldValue(DictionaryContants.TYPE_ID_PLATFORM_INFO, StatusContants.LOGIN_IGNORE_PASSURL.getIndex());

        // 是否跳过检查
        String requestUri = request.getRequestURI();
        requestUri = requestUri.replace(request.getContextPath()+"/", ""); // 去掉工程名/
        if (Pattern.matches(PASS_VALIDATION_URL,requestUri)) {
            chain.doFilter(req, res);
            return ;
        }
        // 获取登录用户信息
        UserVo userVo = (UserVo)request.getSession().getAttribute(SystemContants.USER_INFO);
        if (userVo == null){
            // 用户未登录
            logger.error("您未登录,或登录超时,或您的账号在其他地方登录");
            response.sendRedirect(request.getContextPath() + "/login_to.do");
            return ;
        }
        if (!this.isLogined(request.getSession().getId(), userVo.getUserId())){
            // 用户未登录
            logger.error("您的账号已经在异地登录,请重新登录修改密码。");
            response.sendRedirect(request.getContextPath() + "/login_error.do");
            return ;
        }
        
        chain.doFilter(req, res);
    }
    
     /** 
       * isLogining-用于判断用户是否已经登录 
       * @param        sessionUserName String-登录的用户名 
       * @return boolean-该用户是否已经登录的标志 
       * */  
      public boolean isLogined(String nowSessionId, String nowUserId){  
          boolean result = false;
        BoundHashOperations<String, String, String> boundHashOperations = stringRedisTemplate.boundHashOps(RedisContants.ADMIN_LOGIN_MAP);
        if (boundHashOperations.keys().size() > 0){
            // 当前登线程用户ID, 在集合中没有记录
            String sessionUserId = boundHashOperations.get(nowSessionId); 
            if (!StringUtil.isEmpty(sessionUserId)){
                // 当前使用这个用户ID的线程ID
                String mySessionId = "";
                for (String key :  boundHashOperations.keys()){
                    if(nowUserId.equalsIgnoreCase(boundHashOperations.get(key))){
                        mySessionId = key;
                        break;
                    }
                }
                // 当前线程与登录用户线程ID相等,则登录
                if (nowSessionId.equals(mySessionId)){
                    result = true;
                }
            }
        }
        return result;
      }
    
}
原文地址:https://www.cnblogs.com/a393060727/p/8489648.html