Spring MVC集成Redis会话管理遇配置问题求助
问题分析与解决方案
你的核心问题是Spring Session与Tomcat默认会话机制共存导致的冲突,具体表现为双JSESSIONID、不同方式获取的会话ID不一致。以下是针对性的修复步骤:
一、问题根源
- Tomcat默认会话机制未被完全禁用,仍在生成自己的JSESSIONID Cookie;
- Spring Session的过滤器优先级或配置存在问题,导致部分请求未被Spring Session包装,拿到的是Tomcat原始会话;
- 配置代码中存在隐藏的正则表达式转义错误,可能导致Cookie作用域异常。
二、分步修复
1. 彻底禁用Tomcat本地会话
修改Tomcat的context.xml,添加以下配置,完全关闭Tomcat的会话持久化与默认会话跟踪:
<Context> <!-- 禁用Tomcat本地会话存储 --> <Manager pathname="" /> <!-- 统一Cookie配置,与Spring Session保持一致 --> <SessionCookieConfig name="JSESSIONID" httpOnly="true" secure="true" path="/" /> </Context>
2. 修正Spring Session配置类
调整SessionConfig的配置,修复正则转义错误并补充必要Bean:
@PropertySource(value = "file:${APP_CONFIG}/redis.properties", ignoreResourceNotFound = false) @EnableRedisHttpSession( redisNamespace = "spring:session:demo", cleanupCron = "0 */5 * * * ?", flushMode = FlushMode.IMMEDIATE, saveMode = SaveMode.ALWAYS ) public class SessionConfig extends AbstractHttpSessionApplicationInitializer { @Value("${redis.port}") private Integer redisPort; @Value("${redis.host}") private String redisHost; @Value("${redis.pass}") private String redisPassword; private static final Logger log = LoggerFactory.getLogger(SessionConfig.class); @Bean @Primary public LettuceConnectionFactory redisConnectionFactory() { log.info("初始化Lettuce连接工厂:host={}, port={}", redisHost, redisPort); RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(); redisConfig.setHostName(redisHost); redisConfig.setPort(redisPort); redisConfig.setPassword(RedisPassword.of(redisPassword)); // 规范的密码设置方式 LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig); factory.setEagerInitialization(true); return factory; } @Bean public HttpSessionIdResolver httpSessionIdResolver() { CookieHttpSessionIdResolver resolver = new CookieHttpSessionIdResolver(); DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); cookieSerializer.setCookieName("JSESSIONID"); cookieSerializer.setCookiePath("/"); // 修复正则转义:Java字符串中需用\\.代替\. cookieSerializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); cookieSerializer.setUseBase64Encoding(true); cookieSerializer.setHttpOnly(true); cookieSerializer.setSecure(true); // HTTPS环境开启,HTTP环境可关闭 resolver.setCookieSerializer(cookieSerializer); return resolver; } // 注册会话事件发布器,确保Spring Session完全接管会话生命周期 @Bean public static HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); } }
3. 确保过滤器优先级
继承AbstractHttpSessionApplicationInitializer会自动将SessionRepositoryFilter注册到Servlet过滤器链的最前端,但需注意:
- 若使用XML配置Spring MVC,确保
web.xml中无其他会话相关过滤器优先于Spring Session; - 若存在Spring Security过滤器,需保证Spring Session过滤器在其之前执行(
AbstractHttpSessionApplicationInitializer默认已满足)。
4. 修复会话ID不一致问题
RequestContextHolder获取到Tomcat会话ID的原因是请求未被Spring Session过滤器包装,需:
- 确保所有请求路径都被
/*映射(AbstractHttpSessionApplicationInitializer默认配置); - 仅在请求线程中使用
RequestContextHolder,异步线程需手动传递请求属性; - 验证代码:
// 两种方式获取的会话ID必须一致 String id1 = request.getSession().getId(); String id2 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getRequest().getSession().getId(); System.out.println(id1.equals(id2)); // 应输出true
三、验证步骤
- 启动应用后,查看Redis中的会话键,格式应为
spring:session:demo:sessions:<sessionId>; - 浏览器Cookie中仅存在一个JSESSIONID,且值与Redis中的会话ID一致;
- 所有代码路径获取的会话ID完全相同。
内容的提问来源于stack exchange,提问作者Abhishek Singh
相关产品推荐
相关产品推荐

