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

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

问题描述:

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

I asynchronously invoke method with Spring, using @Async.This method invokes other method annotated with @PreAuthorize, Spring Security Annotation. To make authorization works I have to set SecurityContextHolder mode to MODE_INHERITABLETHREADLOCAL, so that authentication info is passed to the asynchronous call. Everything works fine so far.

但是,当我以其他用户身份注销并登录时,在异步方法 SecurityContextHolder 中存储已注销的旧用户的身份验证信息.它当然会导致不需要的 AccessDenied 异常.同步调用就没有这个问题.

However when I logout and login as a different user, in asynchronous method SecurityContextHolder stores authentication info of the old user, that has bee logged out. It causes of course unwanted AccessDenied exception. There is no such problem with synchronous calls.

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

I have defined <task:executor id="executors" pool-size="10"/>, so may it be a problem that once thread in executors pool has been initialized it will not override authentication information?

我猜 MODE_INHERITABLETHREADLOCAL 不能与线程池一起正常工作.

I guess MODE_INHERITABLETHREADLOCAL doesn't work correctly with thread pool.

作为一种可能的解决方案,您可以尝试子类化 ThreadPoolTask​​Executor 并覆盖其方法以手动传播 SecurityContext,然后声明该执行程序而不是 ;,类似这样:

As a possible solution you can try to subclass ThreadPoolTaskExecutor and override its methods to propagate SecurityContext manually, and then declare that executor instead of <task:executor>, something like this:

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();
            }
        }
    });
}