分布式会话管理实战Session共享与状态管理的完整方案大家好我是迪哥。分布式系统中会话管理是一个经典问题。从传统的 Session 复制到 Redis 共享从 JWT Token 到 OAuth2我们经历了多种方案的演进。今天就聊聊分布式会话管理的最佳实践。会话管理方案对比方案优点缺点适用场景Session 复制简单同步开销大扩展性差小集群粘性会话性能好单点故障问题中等规模Redis 共享高可用易扩展需要额外依赖推荐JWT Token无状态易扩展Token 过期处理复杂API 网关OAuth2标准协议实现复杂多系统集成Redis Session 共享Spring Session 配置Configuration EnableRedisHttpSession(maxInactiveIntervalInSeconds 1800) public class RedisSessionConfig { Bean public LettuceConnectionFactory connectionFactory() { return new LettuceConnectionFactory(localhost, 6379); } }自定义 Session 操作Service public class SessionService { Autowired private RedisTemplateString, Object redisTemplate; private static final String SESSION_PREFIX session:; public void setAttribute(String sessionId, String key, Object value) { redisTemplate.opsForHash().put(SESSION_PREFIX sessionId, key, value); redisTemplate.expire(SESSION_PREFIX sessionId, 30, TimeUnit.MINUTES); } public Object getAttribute(String sessionId, String key) { return redisTemplate.opsForHash().get(SESSION_PREFIX sessionId, key); } public void invalidate(String sessionId) { redisTemplate.delete(SESSION_PREFIX sessionId); } }JWT Token 方案Token 生成与验证Service public class JwtService { private static final String SECRET_KEY your-256-bit-secret; private static final long EXPIRATION_TIME 86400000; // 24小时 public String generateToken(UserDetails userDetails) { MapString, Object claims new HashMap(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public boolean validateToken(String token, UserDetails userDetails) { final String username extractUsername(token); return (username.equals(userDetails.getUsername()) !isTokenExpired(token)); } }Token 刷新机制Service public class TokenRefreshService { Autowired private RedisTemplateString, String redisTemplate; private static final String REFRESH_TOKEN_PREFIX refresh:; public void storeRefreshToken(String username, String refreshToken) { redisTemplate.opsForValue().set(REFRESH_TOKEN_PREFIX username, refreshToken, 7, TimeUnit.DAYS); } public boolean validateRefreshToken(String username, String refreshToken) { String storedToken redisTemplate.opsForValue().get(REFRESH_TOKEN_PREFIX username); return refreshToken.equals(storedToken); } public void removeRefreshToken(String username) { redisTemplate.delete(REFRESH_TOKEN_PREFIX username); } }OAuth2 集成Spring Security OAuth2 配置Configuration EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { Autowired private AuthenticationManager authenticationManager; Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(client-id) .secret(client-secret) .authorizedGrantTypes(password, refresh_token) .scopes(read, write) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(86400); } Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager); } }会话安全防止 Session 固定攻击Service public class SessionFixationProtectionFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpSession session request.getSession(false); if (session ! null !isNewSession(session)) { String currentUser (String) session.getAttribute(user); if (currentUser ! null) { // 登录成功后重新生成 Session ID session.invalidate(); request.getSession(true); } } filterChain.doFilter(request, response); } }会话超时处理Configuration public class SessionTimeoutConfig { Bean public FilterRegistrationBeanSessionTimeoutFilter sessionTimeoutFilter() { FilterRegistrationBeanSessionTimeoutFilter registration new FilterRegistrationBean(); registration.setFilter(new SessionTimeoutFilter()); registration.addUrlPatterns(/*); return registration; } } public class SessionTimeoutFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest req (HttpServletRequest) request; HttpSession session req.getSession(false); if (session ! null session.isNew()) { session.setMaxInactiveInterval(1800); // 30分钟 } chain.doFilter(request, response); } }最佳实践清单维度最佳实践会话存储使用 Redis 共享避免 Session 复制Token 方案短时间 access token 长时间 refresh token安全启用 HTTPS防止会话劫持超时设置合理的超时时间定期清理过期会话监控监控会话数量、活跃用户数、超时率说到会话管理我家那只叫 Docker 的哈士奇最近学会了会话保持——只要我摸过它一次它就会跟着我一整天这粘性会话比我们的 Nginx 还强 我是迪哥我们下期再见