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)
優點:
- 任意節點可處理請求
- 無單點故障
缺點:
- 網絡帶寬消耗大(N2問題)
- 內存占用高(每節點存全量)
- 集群規模受限(通常≤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); }