Assaf Karmon
Assaf Karmon

Reputation: 923

SecurityContextHolder.getContext() NPE when using @Async

I'm trying to convert a sequential series of calls of spring service to be asynchronous.

I have annotated the method with @Async and added taskExecutor configuratinos.

I can see that the method is now being invoked asynchronously but I'm having issues with SecurityContextHolder.getContext() throwing this error:

java.util.concurrent.ExecutionException: java.lang.NullPointerException

Would really appreciate any insights. Thanks!

Upvotes: 2

Views: 3585

Answers (4)

Sheekat
Sheekat

Reputation: 21

If you want to access the security context in the asynchronous calls, You could implement the context aware thread pool executor to store the security context when creating threads like below.

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

configure this as your task executor in the spring configuration. If you're using the Runnable instead of Callable, then override other methods in ThreadPoolTaskExecutor which supports the Runnable execution as well.

Upvotes: 0

Augustin Ghauratto
Augustin Ghauratto

Reputation: 1520

Since Spring-Security 3.2 there is a nice annotation @AuthenticationPrincipal described at the end of this answer. This is the best way to go when you use Spring-Security >= 3.2. You can inject it in various ways. For more information look at this answer

Upvotes: 0

Assaf Karmon
Assaf Karmon

Reputation: 923

I found a solution, changing the startegy to "MODE_INHERITABLETHREADLOCA" solved my problem.

<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>

Upvotes: 3

Brian Kent
Brian Kent

Reputation: 3854

The SecurityContext is stored in a ThreadLocal. So if you access it from a new thread that didn't set it anywhere, then the SecurityContext is null.

Update: Added Threadlocal javadoc link

Upvotes: 7

Related Questions