【问题标题】:SecurityContextHolder.getContext() NPE when using @Async使用 @Async 时的 SecurityContextHolder.getContext() NPE
【发布时间】:2011-10-30 20:26:00
【问题描述】:
我正在尝试将 spring 服务的一系列顺序调用转换为异步。
我已经用@Async 注释了该方法并添加了taskExecutor configuratinos。
我可以看到现在正在异步调用该方法,但我遇到了 SecurityContextHolder.getContext() 抛出此错误的问题:
java.util.concurrent.ExecutionException: java.lang.NullPointerException
非常感谢任何见解。谢谢!
【问题讨论】:
标签:
java
spring
multithreading
spring-security
【解决方案1】:
SecurityContext 存储在ThreadLocal 中。因此,如果您从没有在任何地方设置它的新线程访问它,则 SecurityContext 为空。
更新:添加了 Threadlocal javadoc 链接
【解决方案2】:
我找到了解决方案,将 startegy 更改为“MODE_INHERITABLETHREADLOCA”解决了我的问题。
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.springframework.security.core.context.SecurityContextHolder"/>
<property name="targetMethod" value="setStrategyName"/>
<property name="arguments">
<list>
<value>MODE_INHERITABLETHREADLOCAL</value>
</list>
</property>
</bean>
【解决方案3】:
由于 Spring-Security 3.2 有一个很好的注释 @AuthenticationPrincipal 在这个答案的末尾描述。当您使用 Spring-Security >= 3.2 时,这是最好的方法。您可以通过各种方式注入它。欲了解更多信息look at this answer
【解决方案4】:
如果您想在异步调用中访问安全上下文,您可以在创建线程时实现上下文感知线程池执行器来存储安全上下文,如下所示。
public class CustomExecutor extends ThreadPoolTaskExecutor {
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ScopeAwareCallable<T>(task, SecurityContextHolder.getContext()));
}
public class ScopeAwareCallable<T> implements Callable<T> {
private Callable<T> callableTask;
private SecurityContext securityContext;
public ScopeAwareCallable(Callable<T> task, SecurityContext secContex) {
this.callableTask = task;
this.securityContext = secContex;
}
@Override
public T call() throws Exception {
if(securityContext != null){
SecurityContextHolder.setContext(securityContext);
}
try {
return callableTask.call();
}
finally {
SecurityContextHolder.clearContext();
}
}
}
在 spring 配置中将其配置为您的任务执行器。如果您使用的是 Runnable 而不是 Callable,请覆盖 ThreadPoolTaskExecutor 中也支持 Runnable 执行的其他方法。