我正在尝试将一系列spring服务调用转换为异步。
我已使用@Async
注释了该方法,并添加了taskExecutor configuratinos。
我可以看到该方法现在是异步调用的,但我遇到了SecurityContextHolder.getContext()
抛出此错误的问题:
java.util.concurrent.ExecutionException: java.lang.NullPointerException
非常感谢任何见解。谢谢!
答案 0 :(得分:6)
SecurityContext存储在ThreadLocal中。因此,如果您从未在任何位置设置它的新线程访问它,则SecurityContext为null。
更新:添加了Threadlocal javadoc链接
答案 1 :(得分:3)
我找到了一个解决方案,将策略更改为“MODE_INHERITABLETHREADLOCAL”解决了我的问题。
<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>
答案 2 :(得分:0)
从Spring-Security 3.2开始,在这个答案的最后描述了一个很好的注释@AuthenticationPrincipal
。当您使用Spring-Security&gt; = 3.2时,这是最好的方法。你可以用各种方式注射它。有关详细信息https://confuser.codeplex.com/discussions/428378
答案 3 :(得分:0)
如果要在异步调用中访问安全上下文,可以在创建如下所示的线程时实现上下文感知线程池执行器来存储安全上下文。
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();
}
}
}
在弹簧配置中将其配置为任务执行程序。如果您使用的是Runnable而不是Callable,那么也可以覆盖ThreadPoolTaskExecutor中的其他方法,这些方法也支持Runnable执行。