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

Spring Boot 3迁移JDBC Session与Spring Security异常排查

问题根源与修复方案

核心问题分析

对比两个版本的配置,导致spring_session.principal_name为null的主要原因有以下几点:

  1. 缺失formLogin配置:Spring Boot 3.x的SecurityFilterChain中没有保留2.7.5版本里的formLogin配置,这是表单登录后将认证用户信息绑定到会话的关键环节,缺失后会话无法关联登录用户。
  2. 重复定义HttpSessionIdResolver Bean:同时声明了httpSessionStrategy()和httpSessionIdResolver()两个同类型Bean,Spring容器无法确定优先使用哪个,可能导致会话ID解析异常,影响会话信息的正确存储。
  3. 遗漏RestAuthenticationEntryPoint配置:未将自定义的认证入口点配置到exceptionHandling中,虽不直接导致principal为空,但会影响未认证请求的处理逻辑,属于必要配置遗漏。
  4. 白名单路径不全:3.0.1版本的AUTH_WHITELIST未包含2.7.5中允许的/api/usermanager/image/org/**和/invalidSession*路径,可能导致部分请求拦截异常。

修复后的WebSecurityConfig代码

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        AuthenticationManager manager = authConfig.getAuthenticationManager();
        
        // 保留旧版本的内存用户配置
        UserDetails clientUser = User.withUsername("client")
                .password(passwordEncoder.encode("Client@Venturi"))
                .roles("USER")
                .build();
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(clientUser);
        
        // 配置自定义UserDetailsService的认证Provider
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(userDetailsService);
        daoProvider.setPasswordEncoder(passwordEncoder);
        
        // 将Provider添加到AuthenticationManager
        ProviderManager providerManager = (ProviderManager) manager;
        providerManager.getProviders().add(0, daoProvider);
        providerManager.getProviders().add(1, new DaoAuthenticationProvider(){{
            setUserDetailsService(inMemoryUserDetailsManager);
            setPasswordEncoder(passwordEncoder);
        }});
        
        return manager;
    }

    private static final String[] AUTH_WHITELIST = {
            "/api/usermanager/auth/login",
            "/invalidSession*"
    };

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationFailureHandler authenticationFailureHandler) throws Exception {
        http
            .csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
            .cors()
                .and()
            .httpBasic()
                .and()
            .authorizeHttpRequests(requests -> requests
                    .requestMatchers(AUTH_WHITELIST).permitAll()
                    .requestMatchers(HttpMethod.GET, "/api/usermanager/image/org/**").permitAll()
                    .anyRequest().authenticated()
                )
            .formLogin()
                .permitAll()
                .failureHandler(authenticationFailureHandler)
                .and()
            .sessionManagement()
                .sessionFixation().migrateSession()
                .maximumSessions(1)
                .expiredUrl("/sessionExpired.html")
                .maxSessionsPreventsLogin(false);

        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(
                "/docs/**", "/resources/**", "/static/**"
        );
    }

    @Bean
    public AuthenticationFailureHandler myFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}

关键修改说明

  1. 恢复formLogin配置:重新添加formLogin()相关配置,确保登录成功后将认证用户信息写入会话,Spring Session才能将用户名存储到principal_name字段。
  2. 移除重复Bean:删除httpSessionStrategy()方法,只保留httpSessionIdResolver(),避免容器Bean冲突。
  3. 补全认证入口点:将RestAuthenticationEntryPoint注入并配置到exceptionHandling中,保持与旧版本一致的未认证请求处理逻辑。
  4. 完善白名单:将旧版本中允许的路径全部添加到白名单或requestMatchers中,避免不必要的请求拦截。
  5. 简化AuthenticationManager配置:使用AuthenticationConfiguration创建AuthenticationManager,同时保留旧版本的内存用户和自定义UserDetailsService配置,确保认证逻辑一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 16:15:51