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

并发请求下SecurityContextHolder返回错误用户上下文问题排查

并发请求下SecurityContextHolder返回相同用户对象问题排查

问题现象

多并发请求到达控制器时,即便请求携带的身份头(PRE-AUTH)不同,SecurityContextHolder.getContext().getAuthentication().getPrincipal()偶尔会返回相同的用户对象。

相关代码

控制器代码

@RequestMapping(value = {"/users/{userId}/solveDetail/create"}, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Transactional
public ResponseEntity<CreateSolveDetail> createSolve(@PathVariable("userId") Long userId, @RequestBody CreateSolveDetail createSolveDetail){
        User user =SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

WebSecurityConfig配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityBasicConfig {

    @Autowired
    private Http403ForbiddenEntryPoint http403ForbiddenEntryPoint;
    @Bean
    public Http403ForbiddenEntryPoint http403ForbiddenEntryPoint() {
        return new Http403ForbiddenEntryPoint();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .exceptionHandling()
            .authenticationEntryPoint(http403ForbiddenEntryPoint)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(preAuthFilter(), BasicAuthenticationFilter.class);
        httpSecurity.csrf().disable();
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);
    }
}

自定义PreAuthFilter过滤器

public class PreAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
        String auth = httpServletRequest.getHeader("PRE-AUTH");
        try {
            User user = new ObjectMapper().readValue(auth, User.class);
            return user;
        } catch (Exception e) {
            return new User();
        }
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
        String auth = httpServletRequest.getHeader("PRE-AUTH");
        return auth;
    }
}

已尝试无效方案

  • 配置会话管理为SessionCreationPolicy.STATELESS
  • 设置SecurityContextHolder策略为MODE_THREADLOCAL

问题根源及解决方法

  1. 过滤器未清理线程上下文
    容器线程池复用场景下,若未清理SecurityContext,后续请求会复用之前请求残留的上下文。修改自定义过滤器,在请求处理完成后强制清理:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            super.doFilter(request, response, chain);
        } finally {
            SecurityContextHolder.clearContext();
        }
    }
    
  2. SecurityContext策略设置时机错误
    在configure方法中设置策略仅初始化时执行一次,需在类加载阶段提前设置,确保Spring上下文初始化前生效:

    static {
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);
    }
    
  3. 检查User类线程安全性
    若User类存在共享可变字段,并发场景下会出现对象状态混乱。确保User类字段为不可变类型,或添加线程安全保护。

  4. 确认过滤器注册方式
    检查preAuthFilter()是否在配置类中标记为@Bean,避免单例过滤器的状态共享问题,确保每次请求使用无状态的过滤器实例。

环境信息

  • Spring Boot版本:2.1.6.RELEASE
  • 架构:微服务

内容的提问来源于stack exchange,提问作者utsav anand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:49:16