1. 基于客户端存储(Cookie-Based)
原理:将会话数据直接存储在客户端 Cookie 中
实现:
// Spring Boot 示例
@Bean
public CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName("SESSION");serializer.setUseBase64Encoding(true); // Base64编码serializer.setUseHttpOnlyCookie(true); // 防XSSserializer.setCookiePath("/");serializer.setCookieMaxAge(1800); // 30分钟过期return serializer;
}
优点:
- 服务端完全无状态
- 天然支持水平扩展
- 实现简单
缺点:
- 单Cookie大小限制(4KB)
- 每次请求需传输完整会话数据
- 安全风险(需加密+签名)
- 无法存储敏感数据
适用场景:会话数据量小(<1KB)的安全非敏感场景
2. Session 复制(Session Replication)
原理:集群节点间同步 Session 数据
实现:
<!-- Tomcat server.xml 配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"><Channel className="org.apache.catalina.tribes.group.GroupChannel"><Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"/><Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"><Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/></Sender></Channel>
</Cluster>
同步方式:
- 全量复制:节点变更时广播所有 Session
- 增量复制:仅同步修改的 Session(如 Tomcat DeltaManager)
优点:
- 任意节点可处理请求
- 无单点故障
缺点:
- 网络带宽消耗大(N²问题)
- 内存占用高(每节点存全量)
- 集群规模受限(通常≤8节点)
适用场景:小型集群(≤5节点)且对性能要求不高
3. 集中式存储(Centralized Storage)
原理:会话数据集中存储在外部存储中
3.1 数据库存储
// Spring Session JDBC 配置
@EnableJdbcHttpSession
public class SessionConfig {@Beanpublic JdbcIndexedSessionRepository sessionRepository(DataSource dataSource) {return new JdbcIndexedSessionRepository(dataSource);}
}
表结构:
CREATE TABLE SPRING_SESSION (PRIMARY_ID CHAR(36) PRIMARY KEY,SESSION_ID CHAR(36) NOT NULL,CREATION_TIME BIGINT NOT NULL,LAST_ACCESS_TIME BIGINT NOT NULL,MAX_INACTIVE_INTERVAL INT NOT NULL,EXPIRY_TIME BIGINT NOT NULL
);
3.2 Redis存储(最常用)
// Spring Session Redis
@EnableRedisHttpSession
public class SessionConfig {@Beanpublic LettuceConnectionFactory connectionFactory() {return new LettuceConnectionFactory("redis-cluster", 6379);}
}
Redis数据结构:
Key: spring:session:sessions:<sessionId>
Type: Hash
Fields:creationTime: 1625000000000maxInactiveInterval: 1800lastAccessedTime: 1625001000000sessionAttr::user: {"id":1001,"name":"John"}
优点:
- 支持大规模集群
- 内存读写性能高(Redis 10万+ QPS)
- 数据持久化可选
- 自动过期清理
缺点:
- 引入外部依赖
- 网络延迟增加(约1-3ms)
- 需处理缓存穿透/雪崩问题
优化技巧:
// 本地二级缓存(Caffeine)
@Bean
public SessionRepositoryCustomizer<RedisIndexedSessionRepository> customize() {return repo -> repo.setDefaultMaxInactiveInterval(1800);
}
4. 粘性会话(Sticky Session)
原理:负载均衡器将同一用户的请求固定路由到同一节点
Nginx配置:
upstream backend {ip_hash; # 基于IP的粘性会话server 192.168.1.101:8080;server 192.168.1.102:8080;
}
优点:
- 实现简单
- 无跨节点同步开销
- 兼容传统应用
缺点:
- 节点故障导致会话丢失
- 负载不均(热点用户)
- 扩容时需迁移会话
适用场景:对会话一致性要求不高的传统应用
5. Token-Based 会话(JWT)
原理:无状态会话,信息包含在Token中
实现:
// JWT 生成
String jwt = Jwts.builder().setSubject("user123").claim("roles", "admin,user").setExpiration(new Date(System.currentTimeMillis() + 3600000)).signWith(SignatureAlgorithm.HS256, "secretKey").compact();
Token结构:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"user123","roles":["admin","user"],"exp":1625005000}
Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
优点:
- 完全无状态
- 天然支持跨域
- 减少数据库查询
缺点:
- Token无法主动失效
- 数据量受限(URL长度限制)
- 安全风险(Token泄露)
解决方案:
- 短有效期 + Refresh Token
- 使用黑名单(Redis记录失效Token)
方案选型对比
方案 | 扩展性 | 性能 | 可靠性 | 安全性 | 实现复杂度 |
---|---|---|---|---|---|
客户端存储 | ★★★★★ | ★★★★☆ | ★★☆☆☆ | ★☆☆☆☆ | ★★☆☆☆ |
Session复制 | ★★☆☆☆ | ★★☆☆☆ | ★★★★☆ | ★★★★☆ | ★★★☆☆ |
Redis集中存储 | ★★★★★ | ★★★★☆ | ★★★★☆ | ★★★★☆ | ★★★☆☆ |
数据库存储 | ★★★★☆ | ★★☆☆☆ | ★★★★★ | ★★★★☆ | ★★★☆☆ |
粘性会话 | ★★☆☆☆ | ★★★★☆ | ★★☆☆☆ | ★★★☆☆ | ★☆☆☆☆ |
JWT | ★★★★★ | ★★★★★ | ★★★☆☆ | ★★★☆☆ | ★★★★☆ |
最佳实践建议
-
首选方案
graph LR A[会话数据量] -->|<1KB| B(JWT) A -->|1-10KB| C(Redis集群) A -->|>10KB| D(数据库+本地缓存)
-
安全加固
- Redis启用TLS通信
- 设置
HttpOnly
和Secure
的Cookie - 定期轮换加密密钥
// Cookie安全设置 DefaultCookieSerializer serializer = new DefaultCookieSerializer(); serializer.setUseSecureCookie(true); // 仅HTTPS传输 serializer.setSameSite("Strict"); // 防CSRF
-
高可用设计
- Redis Cluster + Sentinel
- 多级缓存(Redis + 本地Caffeine)
// 多级缓存配置 @Bean public SessionRepository<?> sessionRepository() {MapSessionRepository memoryRepo = new MapSessionRepository();RedisIndexedSessionRepository redisRepo = ...;return new DelegatingSessionRepository(memoryRepo, redisRepo); }
-
性能优化
- 启用
spring.session.redis.flush-mode=immediate
- 使用MessagePack序列化替代JSON
spring:session:redis:flush-mode: immediate # 立即写入namespace: "app:sessions"
- 启用
-
迁移方案
特殊场景处理
-
跨域会话
- 使用OAuth 2.0/JWT
- 设置
SameSite=None
+Secure
-
大Session处理
// 分片存储 public class LargeSessionStrategy {public void saveFragment(String sessionId, String fragmentKey, byte[] data) {redisTemplate.opsForHash().put(sessionId, fragmentKey, data);} }
-
实时踢人下线
// 发布会话失效事件 @Autowired private ApplicationEventPublisher eventPublisher;public void forceLogout(String sessionId) {eventPublisher.publishEvent(new SessionDestroyedEvent(sessionId));redisTemplate.delete("spring:session:sessions:" + sessionId); }