Spring Security 6.2未返回JSESSIONID及会话认证失效问题求助
解决方案:Spring Boot Kotlin 会话认证后403问题
问题根源
你手动通过AuthenticationManager完成了认证,但没有将认证成功的Authentication对象存入SecurityContext并持久化到会话。Spring Security默认的SecurityContextRepository只会自动处理过滤器链内的认证(比如表单登录、OAuth2等),自定义登录端点需要手动完成这一步。
修复步骤
1. 修改LoginController,添加会话持久化逻辑
注入SecurityContextRepository,在认证成功后将认证信息存入上下文并持久化到会话:
@RestController class LoginController( private val authenticationManager: AuthenticationManager, private val securityContextRepository: SecurityContextRepository // 新增注入 ) { data class LoginRequest(val username: String, val password: String) @PostMapping("/auth/login") fun login( @RequestBody loginRequest: LoginRequest, request: HttpServletRequest, response: HttpServletResponse ): ResponseEntity<Void> { val authenticationRequest = UsernamePasswordAuthenticationToken.unauthenticated( loginRequest.username, loginRequest.password ) val authenticationResponse = authenticationManager.authenticate(authenticationRequest) // 将认证信息存入SecurityContext val securityContext = SecurityContextHolder.createEmptyContext() securityContext.authentication = authenticationResponse // 通过SecurityContextRepository持久化到会话 securityContextRepository.saveContext(securityContext, request, response) return ResponseEntity.ok().build() } }
2. 调整SecurityConfig的会话与CORS配置
- 移除单独的
WebMvcConfigurerCORS配置,改用Spring Security内置的CORS配置,避免冲突 - 会话管理使用默认的
IF_REQUIRED(仅在需要时创建会话,无需强制提前创建)
修改后的SecurityConfig:
@Configuration class SecurityConfig { @Autowired lateinit var dataSource: DataSource @Value("\${cors.originPatterns:default}") private val corsOriginPatterns: String = "" @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http { // 整合CORS配置到Spring Security cors { configurationSource { request -> val corsConfig = CorsConfiguration() corsConfig.allowedOriginPatterns = listOf("http://localhost:*") corsConfig.allowedMethods = CorsConfiguration.ALL corsConfig.allowCredentials = true corsConfig } } authorizeHttpRequests { authorize("/auth/*", permitAll) authorize(anyRequest, authenticated) } sessionManagement { sessionCreationPolicy = SessionCreationPolicy.IF_REQUIRED } csrf { disable() } } return http.build() } @Bean fun authenticationManager( userDetailsService: UserDetailsService, passwordEncoder: PasswordEncoder ): AuthenticationManager { val authenticationProvider = DaoAuthenticationProvider() authenticationProvider.setUserDetailsService(userDetailsService) authenticationProvider.setPasswordEncoder(passwordEncoder) return ProviderManager(authenticationProvider) } @Bean fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } @Bean fun users(dataSource: DataSource): UserDetailsManager { return JdbcUserDetailsManager(dataSource) } }
为什么之前的配置无效?
从你的调试日志可以看到,后续请求进入时AnonymousAuthenticationFilter设置了匿名上下文,说明会话中没有保存认证信息:
2024-01-08T18:53:39.470+01:00 DEBUG ... o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
这是因为你手动认证后,没有将authenticationResponse存入SecurityContext并通过SecurityContextRepository持久化到会话,导致会话是空的,后续请求无法识别已认证用户。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

