1. 基于客户端存储(Cookie-Based)
原理 :将会话数据直接存储在客户端 Cookie 中
实现:
java
// Spring Boot 示例
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSION");
serializer.setUseBase64Encoding(true); // Base64编码
serializer.setUseHttpOnlyCookie(true); // 防XSS
serializer.setCookiePath("/");
serializer.setCookieMaxAge(1800); // 30分钟过期
return serializer;
}
优点:
- 服务端完全无状态
- 天然支持水平扩展
- 实现简单
缺点:
- 单Cookie大小限制(4KB)
- 每次请求需传输完整会话数据
- 安全风险(需加密+签名)
- 无法存储敏感数据
适用场景:会话数据量小(<1KB)的安全非敏感场景
2. Session 复制(Session Replication)
原理 :集群节点间同步 Session 数据
实现:
xml
<!-- 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 数据库存储
java
// Spring Session JDBC 配置
@EnableJdbcHttpSession
public class SessionConfig {
@Bean
public JdbcIndexedSessionRepository sessionRepository(DataSource dataSource) {
return new JdbcIndexedSessionRepository(dataSource);
}
}
表结构:
sql
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存储(最常用)
java
// Spring Session Redis
@EnableRedisHttpSession
public class SessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory("redis-cluster", 6379);
}
}
Redis数据结构:
Key: spring:session:sessions:<sessionId>
Type: Hash
Fields:
creationTime: 1625000000000
maxInactiveInterval: 1800
lastAccessedTime: 1625001000000
sessionAttr::user: {"id":1001,"name":"John"}
优点:
- 支持大规模集群
- 内存读写性能高(Redis 10万+ QPS)
- 数据持久化可选
- 自动过期清理
缺点:
- 引入外部依赖
- 网络延迟增加(约1-3ms)
- 需处理缓存穿透/雪崩问题
优化技巧:
java
// 本地二级缓存(Caffeine)
@Bean
public SessionRepositoryCustomizer<RedisIndexedSessionRepository> customize() {
return repo -> repo.setDefaultMaxInactiveInterval(1800);
}
4. 粘性会话(Sticky Session)
原理 :负载均衡器将同一用户的请求固定路由到同一节点
Nginx配置:
nginx
upstream backend {
ip_hash; # 基于IP的粘性会话
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
优点:
- 实现简单
- 无跨节点同步开销
- 兼容传统应用
缺点:
- 节点故障导致会话丢失
- 负载不均(热点用户)
- 扩容时需迁移会话
适用场景:对会话一致性要求不高的传统应用
5. Token-Based 会话(JWT)
原理 :无状态会话,信息包含在Token中
实现:
java
// 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 - 定期轮换加密密钥
java// Cookie安全设置 DefaultCookieSerializer serializer = new DefaultCookieSerializer(); serializer.setUseSecureCookie(true); // 仅HTTPS传输 serializer.setSameSite("Strict"); // 防CSRF
-
高可用设计
- Redis Cluster + Sentinel
- 多级缓存(Redis + 本地Caffeine)
java// 多级缓存配置 @Bean public SessionRepository<?> sessionRepository() { MapSessionRepository memoryRepo = new MapSessionRepository(); RedisIndexedSessionRepository redisRepo = ...; return new DelegatingSessionRepository(memoryRepo, redisRepo); }
-
性能优化
- 启用
spring.session.redis.flush-mode=immediate
- 使用MessagePack序列化替代JSON
yamlspring: session: redis: flush-mode: immediate # 立即写入 namespace: "app:sessions"
- 启用
-
迁移方案
生产 开发 传统单机Session 添加Spring Session依赖 选择存储后端 Redis集群 嵌入式H2 配置负载均衡器 灰度迁移流量
特殊场景处理
-
跨域会话
- 使用OAuth 2.0/JWT
- 设置
SameSite=None
+Secure
-
大Session处理
java// 分片存储 public class LargeSessionStrategy { public void saveFragment(String sessionId, String fragmentKey, byte[] data) { redisTemplate.opsForHash().put(sessionId, fragmentKey, data); } }
-
实时踢人下线
java// 发布会话失效事件 @Autowired private ApplicationEventPublisher eventPublisher; public void forceLogout(String sessionId) { eventPublisher.publishEvent(new SessionDestroyedEvent(sessionId)); redisTemplate.delete("spring:session:sessions:" + sessionId); }
你想要的我全都有:https://pan.q删掉憨子uark.cn/s/75a5a07b45a2
