关于java:在线程重用时将Spring Security与线程池一起使用会导致竞争状态

Using Spring Security with thread pools leads to a race condition when threads are reused

我有一个使用RxJava编写的spring boot微服务。我已经使用spring安全性通过JWT样式令牌来保护它。一切正常,直到我使用RxJava io调度程序添加了线程池。我注意到当线程池与Spring Security一起使用时出现有线行为。当我保存数据时,会同时保存一个userId。当我以userOne登录后创建第一个对象时,将正确创建它。对于userTwo也是一样。然后说我以userThree身份登录并创建一个新数据,将其保存为userOne ID,这是错误的!仅当我在从池中获得的单独线程中执行操作时,此行为才会出现。

我的安全性配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public SecurityConfig(ApplicationProperties applicationProperties) {
    super();
    this.applicationProperties = applicationProperties;
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http.csrf().disable().headers().frameOptions().disable().and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/**")
            .authenticated().antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
            .antMatchers("/swagger-resources/configuration/ui").permitAll().antMatchers("/health/**").permitAll()
            .and()
            .addFilterAfter(new TokenAuthenticationProcessingFilter(
                    new MyAuthenticationProvider(this.applicationProperties),
                   "/api/**", null, new SimpleUrlAuthenticationFailureHandler()), BasicAuthenticationFilter.class);
}

为了简化和清楚起见,删除了不必要的代码。不仅对于RxJava,而且当@Async与spring创建的线程池一起使用时,也存在相同的行为。这是一种比赛条件。

原因可能是此。安全上下文将继承到子线程。父线程从池中获取线程,然后向该线程提交任务。线程将继承安全上下文。任务完成后,线程将释放回池中。说当另一个请求到来时,先前使用的线程被重用。我怀疑安全上下文尚未删除,因此数据保存在旧用户的ID下。

这只是思维导图或想象力。那可能吗?如果是这样,解决该问题的解决方案是什么?有没有办法在线程释放回池之前清除安全上下文? 如何使用@Async和RxJava调度程序实现相同的功能?


您是正确的,当重新使用池中的线程时,该线程中可能仍会存在任何线程本地数据。您正在尝试通过设置全局变量来使用安全证书,而该变量本质上是不安全的。 J2EE程序可以避免这种情况,因为线程模型很简单。

在跨多个线程可以进行事务的环境中,不能使用线程本地安全凭证。

一种可以与RxJava很好配合的方法是将凭据绑定到类上下文,并在该上下文中执行RxJava观察者链,以封闭凭据并确保引用的局部性。您为每个用户创建一个对象,将凭据绑定到该用户,然后在需要凭据的每个步骤中引用该用户的凭据。