SpringBoot+Session+自定义拦截器实现 异地登录 下线

前言

最近在开发系统的时候发现有一个bug,在账号登录成功了以后!再打开一个浏览器登录发现两个都能登录这就使我们的系统安全性大大降低!!

什么是异地登录?

好比qq,王者登录一样,同一个账号在不同设备只能登录一次,前一个会被挤下线

流程

1,前端发起请求登录校验成功后,将当前用户信息存到session中,将当前session会话存储在一个map容器中,然后返回提示消息给前台

2,再次发起登录请求,判断当前map容器是否存在当前的session;如果存在则过期上一个session,存储当前登录会话信息到map容器中

3,再次访问第一个请求,通过拦截器判断当前session 是否有用户登录,有则放行;没有则提示下线用户

代码

controller

    private final Map<String,HttpSession> sessionMap = new HashMap<>();

    @ApiOperation("用户登录")
    @PostMapping("/userLogin")
   public Result<Integer> userLogin(User user, HttpSession session) {
        log.info("登录用户:{}", user);
        User login = userService.login(user);
        if (login == null || !user.getPwd().equals(login.getPwd())) {
            return Result.error("用户名或密码错误");
        }
        // 存储会话
        if(!sessionMap.isEmpty()) {
            // 判断是否登录过
            if(sessionMap.containsKey(login.getUserId().toString())) {
                // 过期上一个session
                sessionMap.get(login.getUserId().toString()).invalidate();
                sessionMap.remove(login.getAccount());
            }
        }
        session.setMaxInactiveInterval(30*60);
        sessionMap.put(login.getUserId().toString(), session);
        session.setAttribute("user", login);
        session.setAttribute(login.getUserId().toString(), login);
        return Result.success("登录成功", login.getUserId());
    }

    @GetMapping("/getUserInfo/{userId}")
    public Object getUserLoginInfo(@PathVariable("userId") String userId) {
       HttpSession session = sessionMap.get(userId);
        try {
            User user = (User) session.getAttribute(userId);
            log.info("当前用户:" + user);
            return user;
        }catch (NullPointerException e) {
            throw new ServiceException(Constants.CODE_NOT_FORBIDDEN,"登录失效");
        }
    }

interceptor

package net.ltbk.music.common;

import lombok.extern.slf4j.Slf4j;
import net.ltbk.music.bean.User;
import net.ltbk.music.common.exception.ServiceException;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @Program: music
 * @ClassName LoginIntercepter
 * @Author: liutao
 * @Description:
 * @Create: 2023-10-18 23:04
 * @Version 1.0
 **/

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        User user= (User) session.getAttribute("user");
        String uri = request.getRequestURI();
        log.info("请求地址:{}",uri);
        log.info("登录用户:{}",user);
        if (user != null) {
            return true;
        }else {
            throw new ServiceException(Constants.CODE_NOT_FORBIDDEN,"用户已下线");
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

千万别忘了放行 登录接口哦

registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/","/login","/register","/user/userLogin","/user/register","/user/logout")

如何配置全局异常

SpringBoot整合Validation统一结果封装和全局异常捕获和参数校验
前言 什么是Validation? 相信大家都用过hibernate-validation 的校验工具,对我们参数校验的工具。 为什么要用它? 相信大家在开发过程中肯定会写if-else判断……

效果图

打开一个chrome登录并访问

打开edge浏览器登录相同账号

刷新chrome浏览器

结尾

到这里我们今天的学习就结束啦!!

欢迎点赞,关注,收藏三连!!

阅读剩余
THE END