添加Spring Security配置后TestRestTemplate的postForEntity请求失败
问题根源
你的问题是线程复用导致SecurityContextHolder中的认证信息残留/未正确更新:TestRestTemplate底层客户端使用线程池复用线程,第一个请求处理完成后,线程绑定的SecurityContext未被清理,第二个请求复用该线程时,权限校验逻辑读取了异常的认证信息,导致访问被拒绝;添加sleep后线程池切换新线程,SecurityContext为空,过滤器正常执行并设置正确的认证信息,因此测试通过。
解决方案
方案1:在过滤器中手动清理SecurityContext
修改UserInfoHeaderFilter的doFilterInternal方法,通过try-finally块确保请求结束后强制清理线程绑定的SecurityContext,彻底避免线程复用带来的残留问题:
@Override protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { try { String headerValue = request.getHeader(HEADER_NAME); if (headerValue != null && headerValue.startsWith("Bearer ")) { setAuthenticationInSecurityContext(headerValue.substring(7)); } filterChain.doFilter(request, response); } finally { // 强制清理线程绑定的SecurityContext SecurityContextHolder.clearContext(); } }
方案2:配置NullSecurityContextRepository
显式配置Spring Security使用NullSecurityContextRepository,确保SecurityContext不会被持久化到Session,且每次请求结束后自动清理:
修改SecurityConfig的filterChain方法:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .addFilterBefore(userInfoHeaderFilter, BasicAuthenticationFilter.class) // 配置SecurityContext不持久化,请求结束自动清理 .securityContext(context -> context .securityContextRepository(new NullSecurityContextRepository()) ) .authorizeHttpRequests(auth -> auth .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**").permitAll() .anyRequest().hasAnyAuthority("SUPER_ADMIN") ) .csrf().disable() .build(); }
补充说明
- Spring Security默认使用
ThreadLocalSecurityContextHolderStrategy,将认证信息绑定到当前线程; - 你的过滤器未处理请求结束后的
SecurityContext清理,且测试场景下默认的SecurityContextPersistenceFilter可能未正确执行清理逻辑; - 两种方案都能从根源解决线程复用导致的认证信息残留问题,方案1更直接针对过滤器逻辑,方案2则通过Spring Security的配置统一管理。
内容的提问来源于stack exchange,提问作者Suule
相关产品推荐
相关产品推荐

