SpringBoot - Cookie & Session 用户登录及登录状态保持功能实现

会话技术

  1. 功能:提供用户登陆成功后的登陆标记 (一次登录,一段时间都登录)
  2. 会话
    1. 定义:包含一次或多次请求和响应的访问操作
    2. 建立会话:用户打开浏览器访问 Web 服务器资源时建立会话
    3. 结束会话:一方断开连接时结束会话
  3. 会话跟踪
    1. 定义:一种维护浏览器状态的方法
    2. 功能:服务器通过会话跟踪来识别多次请求是否来自于同一浏览器
    3. 同一次会话的多次请求间共享数据
  4. 会话跟踪方案
    1. 客户端会话跟踪技术 : Cookie

    2. 服务端会话跟踪技术 : Session

    3. 令牌技术(token)

      令牌 : 用户身份的标识,实际上就是一个字符串


  1. 定义:浏览器访问服务器后,服务器传给浏览器的一段数据,由浏览器保存,后续通信中浏览器也要将 Cookie 发送给服务器
  2. 功能
    1. 识别用户身份 (uid)
    2. 存储用户偏好:让浏览器记住这位访客的特定信息
  3. 工作条件
    1. 浏览器每次访问该服务器,都必须带上 Cookie
    2. 浏览器需要保存 Cookie,不得轻易删除
    3. 不能跨域
  4. 工作流程
    1. 客户端提交一个HTTP请求给服务端
    2. 服务端 Set-Cookie,同时提交响应内容给客户端
    3. 客户端再次向服务器请求,并在请求头中携带一个Cookie
  5. 有效期
    1. Expire 值:Cookie 在生成时就会被指定一个 Expire 值,这就是 Cookie 的生存周期
    2. 立即清除 Cookie:生存周期设置为 "0" 或负值,这样在关闭浏览器时,就马上清除 Cookie,不会记录用户信息,更加安全
  6. 缺点
    1. 数量限制:一个浏览器能创建的 Cookie 数量最多为 300 个,并且每个不能超过 4KB,每个 Web 站点能设置的 Cookie 总数不能超过 20 个
    2. 安全性低:存在跨站点脚本攻击的可能,脚本指令可以读取当前站点的所有 Cookie 内容,并且可以提交到指定服务器重现其功能

问:为什么需要 Cookie 答:web程序使用的HTTP协议是无状态的协议,对于事务处理没有记忆能力,如果后续处理需要前面的信息则必须重传,导致每次连接传送的数据量增大


Session

一、概述

  1. 定义
    1. Session (会话控制),Session 对象存储特定用户会话所需的属性及配置信息
    2. SessionID:客户端第一次请求服务器时,服务器为客户端算出的一个值,存储在 Cookie 中,用于定位用户 Session 在服务器中的位置
  2. 与 Cookie 的区别:Cookie 可以通过伪造来实现登录并进行一些 HTTP 请求,从安全性上来讲,Session 比 Cookie 安全性稍微高一些
  3. 功能 :提高安全性
  4. 有效期:一般为半小时,可以根据需求设定
  5. 缺点:Session 是存储在服务器当中的,所以 Session 过多,会对服务器产生压力

二、相关工具类

  1. HttpSession

    1. 定义:javax.servlet.http.HttpSession 类,是 JavaWeb 自带的工具类

    2. 常用方法

      命令

      功能

      getAttribute("attributeName")

      获取指定名称的属性值

      setAttribute("attributeName", myAttributeObject)

      设置指定名称的属性值

  2. DTO:Data Transfer Object,在 Session 中的 MyClass 应该去掉敏感信息,转为 MyClassDTO 进行传输

三、认证流程

工作流程
  1. 创建 Session :用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
  2. 响应 Session ID:服务器响应请求时,将此 Session 的唯一标识信息 SessionID 返回给浏览器
  3. 存入 Cookie:浏览器接收到服务器返回的 SessionID 信息后,将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
  4. 携带 Cookie:当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息
    1. 存在 Cookie → 自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息
      1. 找到 Session → 证明用户已经登录可执行后面操作
      2. 未找到 Session → 可能是已过期的 Session 或者是假 Cookie
    2. 不存在 Cookie → 说明用户没有登录或者登录失效
原理说明
  1. 登录:客户端提交登录表单

  2. 校验:服务器进行登录校验(如果校验失败在直接抛出异常)

  3. 获取 Session 对象

    1. Request 中已经有 Session → 获取 HttpSession 对象

    2. Request 中还没有 Session → 创建 HttpSession 对象,并生成唯一标识符(SessionId)

      request.getSession() // 如果 request 中有 session 则获取

  4. 更新登录状态:将用户对象 user 以 USER_LOGIN_STATE 为键存储在该 HttpSession 中

    1. USER_LOGIN_STATE 是一个常量,用来标识用户登录状态

      request.getSession().setAttribute(USER_LOGIN_STATE, user); // 更新Session登录状态

  5. 设置过期时间:设置该 HttpSession 的过期时间为 1 小时(即 1 小时如果内没有与该 HttpSession 关联的请求,该 HttpSession 将被自动销毁)

    复制代码
    request.getSession().setMaxInactiveInterval(60 * 60);             // 设定Session过期时间
  6. 回传 SessionId:服务器将 SessionId 写入 Response Headers 中的 Set-Cookie

    复制代码
    HTTP/1.1 200 OK
    Set-Cookie: JSESSIONID=1234567890ABCDEF; Path=/; HttpOnly
  7. 保存 SessionId:客户端将 SessionId 存储在 Cookie 中

  8. 客户端请求资源:客户端登录后,每次访问服务器都会将 SessionId 写在 请求头的 Cookie 属性中

    复制代码
    GET /some/resource HTTP/1.1
    Host: www.example.com
    Cookie: JSESSIONID=1234567890ABCDEF

四、示例

实现逻辑
  1. 创建 DTO 类

    1. 创建 com.projectname.dto.UserDTO 类
    2. 定义 UserDTO 类中的属性 (希望展示的 User 的属性)
  2. 创建 UserHolder 工具类

    1. 创建 com.projectname.utils.UserHolder 类
    2. 定义 ThreadLocal 本地线程 tl
    3. 定义 save / get / remove 方法
  3. 修改 UserServiceImpl 中的返回值

    1. 调用 hutools 的 BeanUtil 工具类,使用其 copyProperties(User, UserDTO.class) 方法进行 DTO 映射

    2. 修改 session 中的返回值 (User → UserDTO)

      session.setAttribute("User", BeanUtil.copyProperties( user, UserDTO.class)

  4. 修改 UserController 中的返回值

    1. UserDTO user = UserHolder.getUser();
  5. 创建拦截器

    1. 获取用户请求的 Token 或其他标识
    2. 验证 Token,并解析出用户信息
    3. 将用户信息存入UserHolder
    4. 在请求完成后清除UserHolder的数据,防止内存泄漏
  6. 配置拦截器:注册拦截器,让其拦截需要验证的路径

示例代码
  1. UserDTO 类(com.projectname.dto.UserDTO)

    复制代码
    // com.projectname.dto.UserDTO
    @Data
    public class UserDTO {
        private Long id;
        private String nickName;
        private String icon;
    }
  2. UserHolder 类(com.myproject.utils.UserHolder.java)

    复制代码
    // com.projectname.utils.UserHolder
    public class UserHolder {
        private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
    
        public static void saveUser(UserDTO user){
            tl.set(user);
        }
    
        public static UserDTO getUser(){
            return tl.get();
        }
    
        public static void removeUser(){
            tl.remove();
        }
    }
  3. 拦截器(com.myproject.config.UserInterceptor.java)

    1. 目标:自动拦截所有请求,如果有用户信息则存入 UserHolder 中

      @Component

      public class UserInterceptor implements HandlerInterceptor {

      复制代码
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          // 1. 从请求头中获取 Token,如果没有 Token 则跳过拦截器(可能是未登录状态)
          String token = request.getHeader("Authorization");
          if (!StringUtils.hasText(token)) {
              return true;
          }
      
          // 2. 校验 Token 并解析用户信息(此处简单模拟,实际应调用 Token 验证服务或工具类)
          UserDTO user = validateTokenAndGetUser(token);
          if (user == null) {        // 如果解析失败,允许继续(未登录状态)
              return true;
          }
      
          // 3. 将用户信息保存到 UserHolder 中
          UserHolder.setUser(user);
          return true;
      }
      
      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
          // 请求处理完成后清理 ThreadLocal,防止内存泄漏
          UserHolder.clear();
      }
      
      	// 模拟 Token 校验并获取用户信息
      private UserDTO validateTokenAndGetUser(String token) {
          // 示例:模拟解析 Token 获取用户
          if ("valid-token".equals(token)) {
              return getUserByToken(token);
          }
          return null; // Token 无效或解析失败
      }

      }

  4. 配置拦截器

    复制代码
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Autowired
        private UserInterceptor userInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(userInterceptor)             // 注册拦截器
                    .addPathPatterns("/**")                      // 拦截所有路径
                    .excludePathPatterns("/login", "/register"); // 排除登录、注册等公开路径
        }
    }

相关推荐
handsome_sai4 小时前
【Java 线程池】记录
java
大学生资源网5 小时前
基于springboot的唐史文化管理系统的设计与实现源码(java毕业设计源码+文档)
java·spring boot·课程设计
掘金码甲哥5 小时前
🚀糟糕,我实现的k8s informer好像是依托答辩
后端
guslegend5 小时前
SpringSecurity源码剖析
java
GoGeekBaird5 小时前
Andrej Karpathy:2025年大模型发展总结
后端·github
uzong6 小时前
听一听技术面试官的心路历程:他们也会有瓶颈,也会表现不如人意
后端
Jimmy6 小时前
年终总结 - 2025 故事集
前端·后端·程序员
roman_日积跬步-终至千里6 小时前
【人工智能导论】02-搜索-高级搜索策略探索篇:从约束满足到博弈搜索
java·前端·人工智能
大学生资源网6 小时前
java毕业设计之儿童福利院管理系统的设计与实现(源码+)
java·开发语言·spring boot·mysql·毕业设计·源码·课程设计
JasmineWr6 小时前
JVM栈空间的使用和优化
java·开发语言