一句话总结
Cookie:浏览器端存储的小数据(4KB),每次请求自动携带,可设置 HttpOnly/Secure。Session:服务端存储的用户会话数据,基于 Cookie 中的 SessionID 关联,占用服务端内存。Token(JWT):无状态认证令牌,Header.Payload.Signature 三段 Base64 编码,服务端不存储,通过签名验证。核心区别:Cookie 是存储机制,Session 是服务端状态,Token 是无状态凭证。分布式系统推荐 Token(JWT),单体应用可用 Session。
初级理解
Cookie
# Cookie 属性:
Set-Cookie: sessionId=abc123; # 键值对
Domain=.example.com; # 作用域
Path=/; # 路径
Max-Age=3600; # 过期时间(秒)
HttpOnly; # 禁止 JS 访问(防 XSS)
Secure; # 仅 HTTPS 传输
SameSite=Lax # 跨站请求控制
# Cookie 特点:
# 1. 大小限制:单个 4KB,每个域名 20-50 个
# 2. 每次请求自动携带(在请求头中)
# 3. 可设置过期时间(Max-Age/Expires)
# 4. 同源策略:Domain + Path 控制作用范围
# Cookie 用途:
# 1. 会话管理(SessionID)
# 2. 个性化设置(语言、主题)
# 3. 用户追踪(广告)
Session
# Session 工作流程:
# 1. 用户登录 → 服务端创建 Session,存储用户信息
# 2. 服务端返回 Set-Cookie: JSESSIONID=xxx
# 3. 浏览器后续请求自动携带 Cookie: JSESSIONID=xxx
# 4. 服务端根据 JSESSIONID 查找 Session,获取用户信息
# Java Servlet Session:
HttpSession session = request.getSession();
session.setAttribute("user", user); // 存储
User user = session.getAttribute("user"); // 读取
session.invalidate(); // 销毁
# Session 特点:
# 1. 数据存储在服务端(内存/Redis/数据库)
# 2. 客户端只存 SessionID(Cookie)
# 3. 相对安全(敏感数据在服务端)
# 4. 占用服务端资源
# 5. 分布式环境需要共享 Session
中级深入
JWT(JSON Web Token)
# JWT 结构:Header.Payload.Signature
# 示例:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.xxx-signature-xxx
# Header(算法类型):
{
"alg": "HS256", # HMAC SHA256
"typ": "JWT"
}
# Payload(数据,不加密,只 Base64 编码):
{
"sub": "1234567890", # 主题(用户ID)
"name": "张三",
"iat": 1516239022, # 签发时间
"exp": 1516240000 # 过期时间
}
# Signature(签名,防篡改):
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret # 服务端密钥
)
# JWT 验证流程:
# 1. 客户端登录 → 服务端验证 → 返回 JWT
# 2. 客户端存储 JWT(localStorage/Cookie)
# 3. 后续请求携带 JWT:Authorization: Bearer xxx
# 4. 服务端验证签名 → 解析 Payload → 获取用户信息
# JWT 特点:
# 1. 无状态:服务端不存储,通过签名验证
# 2. 可扩展:适合分布式/微服务
# 3. 跨域友好:不依赖 Cookie
# 4. 缺点:无法主动失效(除非加黑名单)
Cookie vs Session vs Token 对比
| 对比维度 | Cookie | Session | Token(JWT) |
|---|---|---|---|
| 存储位置 | 浏览器 | 服务端 | 客户端(浏览器) |
| 安全性 | 低(明文,可篡改) | 高(服务端控制) | 中(签名防篡改) |
| 扩展性 | 单机 | 需共享 Session | 天然支持分布式 |
| 跨域 | 受同源限制 | 受同源限制 | 不依赖 Cookie |
| 状态 | 有状态 | 有状态 | 无状态 |
| 大小限制 | 4KB | 无限制 | 无限制(但不宜太大) |
高级进阶
分布式 Session 方案
# 方案1:粘性会话(Sticky Session)
# Nginx 配置 ip_hash,同一用户请求固定到同一台服务器
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
# 缺点:服务器宕机 Session 丢失,负载不均衡
# 方案2:Session 复制(Session Replication)
# Tomcat 集群间复制 Session
# 缺点:网络开销大,不适合大规模集群
# 方案3:集中存储(推荐)
# Session 存 Redis,所有服务器共享
spring:
session:
store-type: redis
# 优点:高可用、易扩展、服务器无状态
# 缺点:Redis 成为单点(需集群)
# 方案4:JWT 无状态(推荐微服务)
# 服务端不存 Session,通过 JWT 签名验证
# 优点:天然分布式,无存储压力
# 缺点:无法主动失效(需配合黑名单或短过期时间)
JWT 安全最佳实践
# 1. 使用 HTTPS 传输(防止 Token 被窃取)
# 2. 设置合理的过期时间
# Access Token:短期(15分钟)
# Refresh Token:长期(7天),用于刷新 Access Token
# 3. 不要在 Payload 中存敏感信息(Base64 可解码)
# 4. 使用强密钥(HMAC-SHA256 至少 256 位)
# 5. 实现 Token 黑名单(Redis 存储已注销的 Token)
# 6. 存储位置选择:
# localStorage:易受 XSS 攻击
# HttpOnly Cookie:防 XSS,但受 CSRF 影响
# (推荐:HttpOnly Cookie + CSRF Token)
实战场景
# 场景1:Spring Boot + JWT 认证
@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto) {
User user = userService.login(dto);
String token = JwtUtil.generate(user.getId());
return Result.ok(token);
}
# 拦截器验证 JWT
public class JwtInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest req, ...) {
String token = req.getHeader("Authorization");
if (token != null && JwtUtil.verify(token)) {
Long userId = JwtUtil.getUserId(token);
// 设置用户上下文
return true;
}
throw new UnauthorizedException();
}
}
# 场景2:Redis 集中存储 Session
# 登录
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(
"session:" + token, user, 30, TimeUnit.MINUTES);
# 验证
User user = redisTemplate.opsForValue()
.get("session:" + token);
面试模拟
面试官:Cookie、Session、Token 有什么区别?
你:Cookie 是浏览器存储机制(4KB),每次请求自动携带。Session 是服务端存储的用户会话数据,通过 Cookie 中的 SessionID 关联。Token(JWT)是无状态认证令牌,服务端不存储,通过签名验证。分布式系统推荐 JWT(无状态),单体应用可用 Session。
面试官:分布式环境下 Session 如何共享?
你:四种方案:1)粘性会话(Nginx ip_hash,同一用户固定服务器);2)Session 复制(Tomcat 集群间复制);3)集中存储(Redis,推荐);4)JWT 无状态(服务端不存 Session,推荐微服务)。方案3和4最常用。