一个账号

最近有个新需求,要实现账户在一个浏览器A登录后,当账户再到第二个浏览器登录的时候,就会把第一个浏览器上的会话销毁,强制去登录。

实现原理

第一次登录

  • 1:在拦截器/过滤器中,获取用户输入的地址,如果是登录,就跳转到登录上,然后生成一个session。在这个session中保存一个key loginUser value就是这个用户的信息
  • 2:设置一个全局map。map中保存了这个用户的姓名(作为key)以及本次session的ID(作为value),在保存前,先判断这个map里面是否保存了相同的key,如果没有,就保存进去

第二次登录

  • 1:当第二次登录的时候,再生成一个session,全局map此时已经存在了同名的key了。根据本次登录的账户名,取出上一次同名的session。然后就简单的从map中移除掉,然后把最新的id添加进去

第一个强制登录

  • 1:当用户进行任何操作的时候,会被拦截器拦截,然后根据key
    loginUser 获取session中的用户信息,如果为空,可能是session过期了

如果不为空,那么就从session中获取同名的用户信息,假如本次的session ID和从全局map中保存的session ID不一样。就说明。当前的用户所具备的session id 已经被从全局map
中移除掉了,那么就重定向到登录页面上。

代码

  • 1:登录
    @PostMapping("/login")
    public String doLogin(HttpServletRequest request, HttpServletResponse response, Model model){

        String userName = request.getParameter("userName");
        String password = request.getParameter("password");


        System.out.println(userName+" : "+password);
        LoginUser user = userService.findUser(userName, password);


        model.addAttribute("name",userName);

        //使用过滤器来进行校验重复,有可能存在多个账号

        HttpSession currentSession = request.getSession();

        currentSession.setAttribute("loginUser",user);


        Map sessionMap = SessionManager.getSessionMap();


        if(!sessionMap.containsKey(user.getUserName())){
            sessionMap.put(user.getUserName(),currentSession.getId());
            System.out.println(user.getUserName()+" 第一次登陆时的session ID是:" + currentSession.getId());

        }else{
            //同名用户上一次登录的时候
            String beforeSessionID = sessionMap.get(user.getUserName());
            if(sessionMap.containsKey(user.getUserName())  && !beforeSessionID.equals(currentSession.getId()) ){
                sessionMap.remove(user.getUserName());
                sessionMap.put(user.getUserName(),currentSession.getId());
          }
        }
        return "hello";
    }


  • 2: 拦截器
public class MyFilter implements Filter {

    private Set excludeUrl = new HashSet<>(Arrays.asList("/login", "/loginOut","/css/**","/fonts/**","/js/**","/favicon.ico"));

    private String[] excludesPattern = new String[]{
            "/login",
            "/favicon.ico",
            "/css/**",
            "/fonts/**",
            "/js/**",
            "/loginOut",
            "/login*",
            "/org.apache*",
    };

    private PathMatcher pathMatcher=new AntPathMatcher();

    public  boolean uriIsExist(String uri){
        for(String path: excludesPattern){
            if(pathMatcher.match(path,uri)){
                return  true;
            }
        }
        return false;
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession currentSession = request.getSession();
        String requestURI = request.getRequestURI();
        if(uriIsExist(requestURI)){
            filterChain.doFilter(servletRequest,servletResponse);
            return;

        }
        LoginUser loginUser = (LoginUser) currentSession.getAttribute("loginUser");
        //如果user为空,可能是session过期,或者是被强制清空了。必须重新登录
        if (loginUser == null) {
            response.sendRedirect(request.getServletContext() + "/login");
        } else {
            Map sessionMap = SessionManager.getSessionMap();
            //获取session中保存的用户名称。
            String userName = loginUser.getUserName();

            //获取已经存在的该用户所保存的sessionID.
            String beforeSessionID = sessionMap.get(userName);
            //如果现在登录成功的同名用户的sessionid和已经存在的id不一致,表示同样名字的session会话在后面产生了,又有一个人登录上来了。
            //本次sessionid已经是属于要被强制下线的session了。

            /**
             * 核心代码是在公用map中,如果存在同名用户,就删除上一次会话,保存新的会话。这个map里面一直保存最新的session ID
             */
            if (!currentSession.getId().equals(beforeSessionID)) {

               String requestPath= request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
               response.sendRedirect(requestPath + "/login");

            } else {
                //表示是最近登录上来的用户的操作。可以放行
                filterChain.doFilter(servletRequest, servletResponse);
            }
        }

    }

    @Override
    public void destroy() {

    }
}





你可能感兴趣的:(一个账号)