You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC集成Redis会话管理遇配置问题求助

问题分析与解决方案

你的核心问题是Spring Session与Tomcat默认会话机制共存导致的冲突,具体表现为双JSESSIONID、不同方式获取的会话ID不一致。以下是针对性的修复步骤:

一、问题根源

  1. Tomcat默认会话机制未被完全禁用,仍在生成自己的JSESSIONID Cookie;
  2. Spring Session的过滤器优先级或配置存在问题,导致部分请求未被Spring Session包装,拿到的是Tomcat原始会话;
  3. 配置代码中存在隐藏的正则表达式转义错误,可能导致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
    

三、验证步骤

  1. 启动应用后,查看Redis中的会话键,格式应为spring:session:demo:sessions:<sessionId>;
  2. 浏览器Cookie中仅存在一个JSESSIONID,且值与Redis中的会话ID一致;
  3. 所有代码路径获取的会话ID完全相同。

内容的提问来源于stack exchange,提问作者Abhishek Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:28:11