Spring Boot 3迁移JDBC Session与Spring Security异常排查
问题根源与修复方案
核心问题分析
对比两个版本的配置,导致spring_session.principal_name为null的主要原因有以下几点:
- 缺失formLogin配置:Spring Boot 3.x的
SecurityFilterChain中没有保留2.7.5版本里的formLogin配置,这是表单登录后将认证用户信息绑定到会话的关键环节,缺失后会话无法关联登录用户。 - 重复定义HttpSessionIdResolver Bean:同时声明了
httpSessionStrategy()和httpSessionIdResolver()两个同类型Bean,Spring容器无法确定优先使用哪个,可能导致会话ID解析异常,影响会话信息的正确存储。 - 遗漏RestAuthenticationEntryPoint配置:未将自定义的认证入口点配置到
exceptionHandling中,虽不直接导致principal为空,但会影响未认证请求的处理逻辑,属于必要配置遗漏。 - 白名单路径不全: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(); } }
关键修改说明
- 恢复formLogin配置:重新添加
formLogin()相关配置,确保登录成功后将认证用户信息写入会话,Spring Session才能将用户名存储到principal_name字段。 - 移除重复Bean:删除
httpSessionStrategy()方法,只保留httpSessionIdResolver(),避免容器Bean冲突。 - 补全认证入口点:将
RestAuthenticationEntryPoint注入并配置到exceptionHandling中,保持与旧版本一致的未认证请求处理逻辑。 - 完善白名单:将旧版本中允许的路径全部添加到白名单或
requestMatchers中,避免不必要的请求拦截。 - 简化AuthenticationManager配置:使用
AuthenticationConfiguration创建AuthenticationManager,同时保留旧版本的内存用户和自定义UserDetailsService配置,确保认证逻辑一致。
内容的提问来源于stack exchange,提问作者Shehan Simen
相关产品推荐
相关产品推荐

