JavaWeb中Cookie与Session详解:从原理到实战
作为后端Java工程师,处理用户会话管理是Web开发的核心技能之一。本文将深入剖析Cookie和Session的底层原理、差异对比,并结合Spring Boot实现完整的用户登录案例,帮助开发者彻底掌握这一核心知识点。
一、Cookie详解
1. Cookie本质与工作原理
-
定义:Cookie是服务器发送到客户端(浏览器)的小型文本文件,存储在客户端本地。
-
工作流:
- 服务器通过
Set-Cookie响应头设置Cookie - 浏览器保存Cookie并在后续请求中通过
Cookie请求头携带 - 服务器解析Cookie完成会话跟踪
- 服务器通过
2. Cookie核心属性
| 属性名 | 作用 | 示例值 |
|---|---|---|
Name=Value | 键值对存储数据 | userId=123 |
Domain | 限定Cookie生效的域名 | .example.com |
Path | 限定Cookie生效的路径 | /api |
Expires/Max-Age | 过期时间(绝对时间/相对秒数) | Wed, 21 Oct 2025 07:28:00 GMT |
Secure | 仅通过HTTPS传输 | Secure |
HttpOnly | 禁止JavaScript访问,防止XSS攻击 | HttpOnly |
SameSite | 防止CSRF攻击(Strict/Lax/None) | SameSite=Lax |
3. Java原生Cookie操作
// 创建Cookie
Cookie cookie = new Cookie("theme", "dark");
cookie.setPath("/");
cookie.setMaxAge(7 * 24 * 60 * 60); // 7天
cookie.setHttpOnly(true);
cookie.setSecure(true); // 仅HTTPS生效
// 添加到响应
response.addCookie(cookie);
// 读取Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("theme".equals(c.getName())) {
System.out.println("用户主题偏好: " + c.getValue());
}
}
}
二、Session详解
1. Session本质与工作原理
-
定义:Session是服务器端维护的会话状态容器,通过唯一标识符(Session ID)关联客户端请求。
-
工作流:
- 客户端首次请求时,服务器生成Session ID
- 通过Cookie(默认)或URL重写将Session ID返回客户端
- 后续请求携带Session ID,服务器恢复会话状态
2. Session实现机制
-
存储方式:
- 内存存储:默认实现(如Tomcat的
StandardManager) - 分布式存储:Redis(Spring Session集成)
- 数据库存储:JDBC实现
- 内存存储:默认实现(如Tomcat的
-
生命周期:
- 创建:
request.getSession()首次调用时 - 销毁:超时(默认30分钟)、调用
invalidate()、服务器重启
- 创建:
3. Spring Boot集成Session
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestParam String username,
@RequestParam String password,
HttpSession session) {
// 模拟认证
if ("admin".equals(username) && "123456".equals(password)) {
// 存储用户信息到Session
session.setAttribute("user", User.builder()
.id(1L)
.username(username)
.loginTime(LocalDateTime.now())
.build());
// 设置Session超时时间(秒)
session.setMaxInactiveInterval(1800); // 30分钟
return ResponseEntity.ok("登录成功");
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
@GetMapping("/profile")
public ResponseEntity<User> getProfile(HttpSession session) {
User user = (User) session.getAttribute("user");
if (user != null) {
return ResponseEntity.ok(user);
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
@GetMapping("/logout")
public ResponseEntity<String> logout(HttpSession session) {
session.invalidate();
return ResponseEntity.ok("已退出登录");
}
}
// 用户实体类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String username;
private LocalDateTime loginTime;
}
三、Cookie vs Session深度对比
| 特性 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端浏览器 | 服务器端 |
| 数据大小限制 | 单个Cookie ≤4KB,总数有限制 | 理论上无限制(受服务器内存/存储影响) |
| 安全性 | 较低(可被篡改,需配合HttpOnly/Secure) | 较高(存储在服务端) |
| 跨域支持 | 需配置Domain属性 | 天然支持跨域(通过Session共享) |
| 典型应用场景 | 记住用户偏好、购物车 | 用户登录状态、购物车(敏感数据) |
四、实战案例:基于Session的用户登录系统
1. 系统架构
- 前端:HTML表单提交用户名/密码
- 后端:Spring Boot处理认证逻辑
- Session管理:存储用户登录状态
- 安全增强:CSRF防护、HTTPS、Session固定攻击防护
2. 完整代码实现
2.1 配置类(启用CSRF防护)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.csrf().disable(); // 开发环境禁用,生产环境需配置CSRF令牌
}
}
2.2 控制器
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginForm() {
return "login";
}
@PostMapping("/login")
public String processLogin(@RequestParam String username,
@RequestParam String password,
HttpSession session,
RedirectAttributes redirectAttributes) {
// 模拟认证
if ("admin".equals(username) && "123456".equals(password)) {
session.setAttribute("user", User.builder()
.id(1L)
.username(username)
.build());
return "redirect:/dashboard";
}
redirectAttributes.addFlashAttribute("error", "用户名或密码错误");
return "redirect:/login";
}
@GetMapping("/dashboard")
public String showDashboard(HttpSession session, Model model) {
User user = (User) session.getAttribute("user");
if (user == null) {
return "redirect:/login";
}
model.addAttribute("user", user);
return "dashboard";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/login";
}
}
2.3 Thymeleaf模板(login.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>用户登录</title>
</head>
<body>
<div th:if="${error}" class="alert alert-danger">
<span th:text="${error}">错误信息</span>
</div>
<form method="post" th:action="@{/login}">
<div>
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div>
<label>密码:</label>
<input type="password" name="password" required>
</div>
<button type="submit">登录</button>
</form>
</body>
</html>
五、生产环境最佳实践
-
Cookie安全增强:
// 强制使用Secure和HttpOnly cookie.setSecure(true); cookie.setHttpOnly(true); // 防止CSRF攻击 cookie.setSameSite("Strict"); -
Session分布式部署:
// Spring Session集成Redis @EnableRedisHttpSession public class RedisSessionConfig { @Bean public LettuceConnectionFactory connectionFactory() { return new LettuceConnectionFactory(); } } -
Session固定攻击防护:
// 登录成功后重新生成Session ID @PostMapping("/login") public String login(...) { // ...认证逻辑... HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); // 销毁旧Session } session = request.getSession(true); // 创建新Session session.setAttribute("user", user); // ... } -
性能优化:
- 合理设置Session超时时间
- 敏感数据存储在服务端,非敏感数据可存储在加密的Cookie中
- 使用Token(如JWT)替代Session实现无状态认证
六、总结
Cookie和Session是Web会话管理的两大基石,开发者需根据业务场景选择合适方案:
- 使用Cookie:存储非敏感数据(如主题偏好)、实现"记住我"功能
- 使用Session:存储用户登录状态、购物车等敏感数据