SpringBoot3.3.5升级后遇401:SecurityContext无Authentication对象
SpringBoot 3.3.5(适配Spring Security 6)升级后SecurityContext认证对象丢失导致401问题解决
问题现象
升级后系统出现401错误,报错信息为:An Authentication object was not found in the SecurityContext。日志显示认证对象已标记authenticated=true,但后续过滤器链执行时,SecurityContext被重新创建为无认证对象的实例,最终授权环节失败。
问题根源分析
- SecurityContextRepository配置冲突:配置了
DelegatingSecurityContextRepository同时包含RequestAttributeSecurityContextRepository和HttpSessionSecurityContextRepository,但系统设置为SessionCreationPolicy.STATELESS,HttpSession不存在,后续过滤器尝试从Session获取SecurityContext失败,重新创建空上下文。 - SecurityContextHolder操作不当:在
CustomAuthenticationFilter和CustomAuthenticationProvider中重复设置MODE_INHERITABLETHREADLOCAL策略,且Provider中调用SecurityContextHolder.clearContext()清空了已设置的认证对象,未同步到请求属性中。 - 认证上下文未持久化:认证完成后仅设置了
SecurityContextHolder,但未将上下文保存到配置的RequestAttributeSecurityContextRepository,导致后续过滤器无法从正确位置获取认证信息。
修复方案
1. 修正SecurityContextRepository配置
无状态场景下直接使用RequestAttributeSecurityContextRepository即可,无需混合Session存储:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth.requestMatchers("/", "/**/*.html", "/**/*.css", "/**/*.js" , "/ecafPostapid/**", "/ecafPostpaid/healthtest/checktest","/ecafPostpaid/ecafPostpaid/health" ).permitAll().anyRequest().authenticated()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(ex -> ex.authenticationEntryPoint(this.ecafAuthenticationEntryPoint)) .anonymous(anonymous -> anonymous.disable()) .securityContext(securityContext -> securityContext .securityContextRepository(new RequestAttributeSecurityContextRepository())); httpSecurity.addFilterBefore(new CustomAuthenticationFilter(this.authenticationManager, this.encDecAlgorithm, this.environment, this.filterManager), UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); }
2. 调整CustomAuthenticationFilter逻辑
移除不必要的策略设置,认证完成后将上下文保存到Repository:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { try { request.setAttribute("startTime", System.currentTimeMillis()); MDC.put("requestID", request.getHeader("X-eCAF-RequestId")); CustomAuthentication authentication = new CustomAuthentication(); String stringToken = request.getHeader("x-access-token"); String source = Objects.toString(request.getHeader("source")); authentication.setPrincipal(stringToken); authentication.setSource(source); SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(authentication); SecurityContextHolder.setContext(context); Authentication authenticated = this.authenticationManager.authenticate(authentication); context.setAuthentication(authenticated); // 将认证上下文保存到请求属性中 SecurityContextRepository repository = new RequestAttributeSecurityContextRepository(); repository.saveContext(context, request, response); LOGGER.info("The Authentication {}", authenticated); chain.doFilter(request, response); } catch (Exception e) { MDC.clear(); throw e; } finally { SecurityContextHolder.clearContext(); MDC.clear(); } }
3. 修正CustomAuthenticationProvider逻辑
移除直接操作SecurityContextHolder的代码,Provider仅负责返回已认证对象:
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UsernamePasswordAuthenticationToken userNameToken = new UsernamePasswordAuthenticationToken( jwtToken.getPrincipal(), jwtToken.getCredentials(), jwtToken.getAuthorities()); jwtPayload.setToken(accessToken); userNameToken.setDetails(jwtPayload); userNameToken.setAuthenticated(true); if (userNameToken.isAuthenticated()) { ((CustomAuthentication) authentication).setAuthenticated(true); } return userNameToken; }
关键说明
- 在Spring Security 6中,无状态场景下推荐使用RequestAttributeSecurityContextRepository,将SecurityContext存储在请求属性中,避免Session相关问题。
- 自定义认证过滤器中,认证完成后必须将SecurityContext保存到配置的Repository,后续过滤器才能正确获取认证信息。
- AuthenticationProvider的职责是验证请求并返回已认证对象,不应直接操作SecurityContextHolder,该操作应由过滤器或框架处理。
内容的提问来源于stack exchange,提问作者Pragati Yadav
相关产品推荐
相关产品推荐

