使用Spring Security和线程池会在重用线程时导致竞争条件

时间:2018-03-30 13:34:31

标签: java multithreading spring-boot spring-security rx-java

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

我的安全配置文件是这样的:

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创建的线程池一起使用时,也存在相同的行为。这是一种竞争条件。

原因可能是这个。 securty上下文继承到子线程。父线程从池中获取线程并将任务提交给该线程。该线程继承了安全上下文。任务完成后,线程将被释放回池中。假设当另一个请求到来之前使用的线程被重用。我怀疑安全上下文没有被删除,因此数据保存在旧用户的ID下。

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

1 个答案:

答案 0 :(得分:1)

您是正确的,当重新使用池中的线程时,线程中可能仍会存在任何线程本地数据。您试图通过设置全局变量来使用安全凭证,这本质上是不安全的。 J2EE程序可以逃脱,因为线程模型是......过于简单。

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

使用RxJava可以很好地工作的一种方法是将凭证绑定到类上下文,并在该上下文中执行RxJava观察者链,关闭凭据并确保引用的位置。您为每个用户创建一个对象,将凭据绑定到用户,然后在需要凭据的每一步引用用户凭据。