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

使用@EnableJdbcHttpSession时PRINCIPAL_NAME为空及用户主体丢失问题

问题原因与解决方案

嘿,我来帮你拆解下这个问题——这其实是在传统非Spring Boot的Spring应用里,整合Spring Session JDBC和Spring Security时很容易踩的一个坑。核心问题是启用@EnableJdbcHttpSession后,Spring Security的认证信息没有被正确存储到数据库会话中,导致前端取不到、数据库PRINCIPAL_NAME字段为空。下面是具体的原因和解决办法:

1. 默认序列化器不支持Spring Security的Authentication对象

Spring Session JDBC默认使用JdkSerializationHttpSessionSerializer来序列化会话对象,但这个序列化器对Spring Security的Authentication(尤其是里面的Principal对象)支持不好,经常会导致序列化时丢失认证信息,自然数据库的PRINCIPAL_NAME字段也无法被填充。

解决办法:改用Jackson序列化器并注册Spring Security模块

  • 首先在pom.xml中添加Spring Security的Jackson支持依赖:
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jackson2</artifactId>
        <version>和你的Spring Security版本保持一致</version>
    </dependency>
    
  • 然后创建自定义的Spring Session配置类,替换默认的序列化器:
    @Configuration
    @EnableJdbcHttpSession
    public class CustomJdbcHttpSessionConfig extends JdbcHttpSessionConfiguration {
        @Override
        public void setHttpSessionSerializer(HttpSessionSerializer httpSessionSerializer) {
            ObjectMapper objectMapper = new ObjectMapper();
            // 注册Spring Security的Jackson模块,让它能正确序列化认证对象
            objectMapper.registerModule(new SpringSecurityJackson2Module());
            // 开启默认类型信息,确保反序列化时能识别具体的Authentication类型
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            super.setHttpSessionSerializer(new Jackson2HttpSessionSerializer(objectMapper));
        }
    }
    
    这样配置后,Spring Session会用Jackson来序列化会话中的所有对象,包括Spring Security的认证信息,既能保留完整的Principal数据,也能让数据库的PRINCIPAL_NAME字段被正确填充。

2. 过滤器顺序配置错误

在非Spring Boot应用中,过滤器的执行顺序至关重要。如果Spring Security的过滤器在Spring Session的SessionRepositoryFilter之前执行,那么认证信息还没被存入会话,就被Spring Session的过滤器处理了,导致最终存储到数据库的会话里没有认证数据。

解决办法:确保SessionRepositoryFilter先于Spring Security过滤器执行

方式一:Java配置

如果用Java配置Web应用,在初始化类中指定过滤器顺序:

@Configuration
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Filter[] getServletFilters() {
        // 获取Spring Session的会话过滤器
        SessionRepositoryFilter<?> sessionFilter = getApplicationContext().getBean(SessionRepositoryFilter.class);
        // 获取Spring Security的过滤器代理
        DelegatingFilterProxy securityFilter = new DelegatingFilterProxy("springSecurityFilterChain");
        // 顺序:Session过滤器在前,Security过滤器在后
        return new Filter[]{sessionFilter, securityFilter};
    }
}

方式二:web.xml配置

如果用传统的web.xml配置,调整过滤器的定义顺序:

<!-- 先配置Spring Session的过滤器 -->
<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

<!-- 再配置Spring Security的过滤器 -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3. 数据库表结构不符合Spring Session要求

如果你的SPRING_SESSION表不是用Spring Session官方提供的脚本创建的,可能缺少PRINCIPAL_NAME字段或者字段类型不正确,导致无法存储主体信息。

解决办法:使用官方标准表结构

以MySQL为例,执行以下SQL创建正确的表结构:

CREATE TABLE SPRING_SESSION (
  PRIMARY_ID CHAR(36) NOT NULL,
  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,
  PRINCIPAL_NAME VARCHAR(100),
  PRIMARY KEY (PRIMARY_ID),
  UNIQUE KEY SPRING_SESSION_IX1 (SESSION_ID),
  KEY SPRING_SESSION_IX2 (EXPIRY_TIME),
  KEY SPRING_SESSION_IX3 (PRINCIPAL_NAME)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

CREATE TABLE SPRING_SESSION_ATTRIBUTES (
  SESSION_PRIMARY_ID CHAR(36) NOT NULL,
  ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
  ATTRIBUTE_BYTES BLOB NOT NULL,
  PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
  CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

按照这三个步骤逐一排查,应该就能解决你的问题了——既能让${sessionScope.SPRING_SECURITY_CONTEXT.authentication.principal}正常显示,也能让数据库的PRINCIPAL_NAME字段填充正确的值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:07:57