Spring Security和@Async(经过身份验证的用户混淆)

Luk*_*ren 33 spring asynchronous spring-security

我使用@Async异步调用Spring方法.该方法调用其他使用@PreAuthorize,Spring Security Annotation注释的方法.要使授权工作,我必须将SecurityContextHolder模式设置为MODE_INHERITABLETHREADLOCAL,以便将身份验证信息传递给异步调用.到目前为止一切正常.

但是,当我以不同的用户身份注销和登录时,在异步方法中,SecurityContextHolder会存储已注销的旧用户的身份验证信息.它当然会引起不必要的AccessDenied异常.同步调用没有这样的问题.

我已定义<task:executor id="executors" pool-size="10"/>,所以可能是一个问题,一旦执行程序池中的线程已初始化,它将不会覆盖身份验证信息?

axt*_*avt 34

我想MODE_INHERITABLETHREADLOCAL与线程池无法正常工作.

作为一种可能的解决方案,您可以尝试子类化ThreadPoolTaskExecutor并覆盖其方法以SecurityContext手动传播,然后声明该执行器而不是<task:executor>像这样:

public void execute(final Runnable r) {
    final Authentication a = SecurityContextHolder.getContext().getAuthentication();

    super.execute(new Runnable() {
        public void run() {
            try {
                SecurityContext ctx = SecurityContextHolder.createEmptyContext();
                ctx.setAuthentication(a);
                SecurityContextHolder.setContext(ctx);
                r.run();
            } finally {
                SecurityContextHolder.clearContext();
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 请检查`org.springframework.security.concurrent.DelegatingSecurityContextExecutorService` (5认同)
  • 请参阅以下@Ralph的解决方案,参考使用DelegatingSecurityContextAsyncTaskExecutor (2认同)

Max*_*Max 27

我也遇到了这个问题。使用DelegatingSecurityContextAsyncTaskExecutor. 调用 initialize() 方法也很重要,否则会抛出错误。

// define the TaskExecutor as a bean
@Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {

  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(20);
  executor.setMaxPoolSize(1000);
  executor.setWaitForTasksToCompleteOnShutdown(true);
  executor.setThreadNamePrefix("Async-");
  executor.initialize(); // this is important, otherwise an error is thrown
  return new DelegatingSecurityContextAsyncTaskExecutor(executor); // use this special TaskExecuter
}
Run Code Online (Sandbox Code Playgroud)
// the method in your business logic which is called async
@Override
@Async("threadPoolTaskExecutor")
public void yourLogic() {
  [..]
}
Run Code Online (Sandbox Code Playgroud)


Ral*_*lph 13

这只是一个需要进一步调查的提示(我太累了,但也许有人发现这对今后的调查很有用):

今天我绊倒org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor看到GitHub上.

看起来他的设计是委托安全上下文,以便通过@Async调用"传递" .

另外看看这篇文章:Spring Security 3.2 M1亮点,Servlet 3 API支持听起来与它有很强的关系.


rin*_*nce 6

使用拉尔夫和奥克的信息 -

如果您想让@Async使用标准任务执行程序标记,您可以像这样设置Spring XML配置

<task:annotation-driven executor="_importPool"/>
<task:executor id="_importPool" pool-size="5"/>

<bean id="importPool"
          class="org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor">
     <constructor-arg ref="_importPool"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

然后在@Async方法中,指定要使用的池

@Async("importPool")
public void run(ImportJob import) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

这应该工作,所以当你调用@Async方法时,线程池线程将使用与调用线程相同的安全上下文


Kir*_*ill 5

正如已经提到的,对于池化线程环境DelegatingSecurityContextAsyncTaskExecutor应该使用,而不是MODE_INHERITABLETHREADLOCAL(阅读此处)。

为 Spring Boot 项目留下简单的DelegatingSecurityContextAsyncTaskExecutor配置,该项目将仅使用默认的 Spring Boot 池来执行异步任务:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    private final ThreadPoolTaskExecutor defaultSpringBootAsyncExecutor;

    public AsyncConfig(ThreadPoolTaskExecutor defaultSpringBootAsyncExecutor) {
        this.defaultSpringBootAsyncExecutor = defaultSpringBootAsyncExecutor;
    }

    @Override
    public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextAsyncTaskExecutor(defaultSpringBootAsyncExecutor);
    }
}
Run Code Online (Sandbox Code Playgroud)