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 | 由容器超时控制 |
| @SessionAttributes | Session(模型映射) | Model + SessionStatus 清理 | 控制器会话流程内 |
实践建议
- API 接口优先用无状态方案(令牌/Header),Session 适合服务端渲染与单体 Web 应用;水平扩展时要处理会话共享(粘性会话或集中存储,如 Spring Session,属后续主题)。
- 别把大对象塞 Session;存最小必要信息(用户 id),需要时再查库。
- 登录成功建议换新会话 ID(会话固定攻击防护),Servlet 容器通常提供相应 API/配置,细节以容器文档为准。
Cookie 是「浏览器端的名片」,Session 是「服务端的档案柜」,JSESSIONID 把两者拴在一起。@CookieValue 收、@SessionAttributes 存、@SessionAttribute 读、SessionStatus 清——四个注解覆盖了会话流水的收发放管。