Cookie 与 Session、@SessionAttributes 基础

HTTP 无状态,但业务需要「记得你是谁」。Cookie 是浏览器本地存储的小数据,Session 是服务端会话数据,靠 Cookie 里携带的会话 ID(如 JSESSIONID)关联。Spring MVC 为两者提供了便捷的注解式访问。

Cookie 的读写

读:方法参数上标 @CookieValue;写:通过 HttpServletResponse 下发:

@RestController
public class VisitApi {
    @GetMapping("/api/visit")
    public String visit(@CookieValue(value = "lang", defaultValue = "zh") String lang,
                        HttpServletResponse response) {
        jakarta.servlet.http.Cookie cookie = new Cookie("lang", "en");
        cookie.setMaxAge(3600);        // 有效期 1 小时(秒)
        cookie.setPath("/");           // 作用路径
        cookie.setHttpOnly(true);      // 禁止 JS 读取,防 XSS 窃取
        response.addCookie(cookie);    // Set-Cookie: lang=en; ...
        return "上次语言:" + lang;
    }
}

Cookie 属性(Secure、HttpOnly、SameSite、Max-Age)逐个 setter 设置即可;SameSite 等较新属性的可用性取决于容器版本,以官方文档为准。

Session 的基本用法

方法直接声明 HttpSession 参数,Spring 注入当前会话(首次访问时容器才真正创建):

@PostMapping("/api/login")
public String login(@RequestParam String username, HttpSession session) {
    session.setAttribute("loginUser", username);   // 写入会话
    return "登录成功";
}

@GetMapping("/api/me")
public Object me(HttpSession session) {
    return session.getAttribute("loginUser");      // 读取
    // session.invalidate();  退出登录时销毁整个会话
}
  • 会话 ID 由容器通过 Cookie(默认 JSESSIONID)带给浏览器,之后的请求凭它找回服务端数据。
  • Session 默认惰性创建:只有调用 getSession()/setAttribute 时才真正建会话。
  • 会话超时由容器配置(web.xml session-config 或 Tomcat 的 sessionTimeout),以容器文档为准。

@SessionAttributes:控制器内的会话模型

想简化「表单跨请求暂存到会话」这类场景,用 @SessionAttributes 把指定名字的模型属性自动存进 Session:

@Controller
@SessionAttributes("cart")                 // 模型里的 cart 自动同步到 Session
public class CartController {
    @GetMapping("/cart/add")
    public String add(@RequestParam String item, Model model) {
        Cart cart = (Cart) model.getAttribute("cart");
        if (cart == null) {
            cart = new Cart();
            model.addAttribute("cart", cart); // 首次加入模型,同时进 Session
        }
        cart.add(item);
        return "redirect:/cart/show";
    }

    @GetMapping("/cart/clear")
    public String clear(SessionStatus status) {
        status.setComplete();                // 清掉 @SessionAttributes 管理的会话属性
        return "redirect:/cart/show";
    }
}

@SessionAttributes 只对「该控制器方法加入模型、且名字匹配」的属性生效;清除它用 SessionStatus.setComplete()(只清 @SessionAttributes 相关属性,不影响 HttpSession 里手动放的数据)。

@SessionAttribute:读已有的会话数据

与 @SessionAttribute(s) 只差一个字母,语义完全不同——前者读 Session 中已存在的属性(不存在抛异常,可用 required=false):

@GetMapping("/api/profile")
public String profile(@SessionAttribute(value = "loginUser", required = false) String loginUser) {
    return loginUser == null ? "未登录" : "当前用户:" + loginUser;
}

对比速查

机制存放位置读取注解/方式生命周期
Cookie浏览器@CookieValue由 Max-Age 控制
HttpSession服务端HttpSession 参数 / @SessionAttribute由容器超时控制
@SessionAttributesSession(模型映射)Model + SessionStatus 清理控制器会话流程内

实践建议

  • API 接口优先用无状态方案(令牌/Header),Session 适合服务端渲染与单体 Web 应用;水平扩展时要处理会话共享(粘性会话或集中存储,如 Spring Session,属后续主题)。
  • 别把大对象塞 Session;存最小必要信息(用户 id),需要时再查库。
  • 登录成功建议换新会话 ID(会话固定攻击防护),Servlet 容器通常提供相应 API/配置,细节以容器文档为准。

Cookie 是「浏览器端的名片」,Session 是「服务端的档案柜」,JSESSIONID 把两者拴在一起。@CookieValue 收、@SessionAttributes 存、@SessionAttribute 读、SessionStatus 清——四个注解覆盖了会话流水的收发放管。

笔记加载中…