41

I'm using asynchronous methods in my service (Spring 3 @Async annotation). And I've got a problem - spawned thread doesn't have security context. Cause of it is Spring Security by default uses SecurityContextHolder.MODE_THREADLOCAL strategy for its context holder. But I need to use SecurityContextHolder.MODE_INHERITABLETHREADLOCAL strategy. For the moment I set up strategy in my AuthenticationSuccessHandler. But in my point of view it's not a good practice.

So how can I set it up in context configuration file?
Version of spring security is 3.0.0.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
viator
  • 1,413
  • 3
  • 14
  • 25
  • 2
    Possible duplicate of [Spring Security and @Async](http://stackoverflow.com/questions/5246428/spring-security-and-async) – rince Jan 06 '16 at 15:50
  • 6
    Note that for web applications, this is the wrong question to ask. Using `MODE_INHERITABLETHREADLOCAL` can result in a thread pool containing threads with the wrong security context for async tasks. A better approach is to use an executor that delegates the security context when it runs the task. See here: [Spring Security and @Async (Authenticated Users mixed up)](https://stackoverflow.com/questions/5246428/spring-security-and-async-authenticated-users-mixed-up) – Bernie Jul 26 '18 at 02:49

4 Answers4

47

You can set the environment variable spring.security.strategy to MODE_INHERITABLETHREADLOCAL. You could also have a simple bean that during your web applications startup calls SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL) and initialize that value in your context configuration file.

SecurityContextHolder API

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Gandalf
  • 9,648
  • 8
  • 53
  • 88
  • 23
    Thanks for your tip about simple bean. But I found more convenient solution for me - I added to my security context following code: – viator Aug 13 '10 at 08:46
  • I'm still having no httpRequest.getRemoteUser() with this bean configured. – javadev Feb 26 '18 at 17:58
  • 4
    Beware of the implications: if using a threadpool, you should also use setTaskDecorator() as explained here https://github.com/spring-projects/spring-security/issues/6856#issuecomment-518787966 I think is is REALLY important. – Francois Marot Nov 04 '19 at 10:36
  • 3
    @FrancoisMarot Thank you for your thread pool hint! It saved my rear end. With `SecurityContextHolder.MODE_INHERITABLETHREADLOCAL`, my thread pool sometimes didn't get the user context. With `executor.setTaskDecorator(runnable -> new DelegatingSecurityContextRunnable(runnable));`, it seems to always do. – Karsten Silz Sep 10 '20 at 18:22
32

The java config for @viator 's answer if it helps you.

@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
    MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
    methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
    methodInvokingFactoryBean.setTargetMethod("setStrategyName");
    methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
    return methodInvokingFactoryBean;
}
Matt Broekhuis
  • 1,985
  • 2
  • 26
  • 35
  • 1
    Late comment :-) Is there an advantage to using this over some sort of @PostConstruct somewhere in a configuration class for security? – Nick H Jan 16 '17 at 20:15
  • I'd have to see an example. Postconstruct won't make a spring bean so I'm not sure how? – Matt Broekhuis Jan 16 '17 at 20:17
  • I meant like inside @PostConstruct calling SecurityContextHolder.setStrategyName(); It's all static so my guess is it would probably work as well. I'll have to give it a shot. – Nick H Jan 16 '17 at 22:43
  • 1
    There's a complete explanation in a great post here https://dev.to/spooz/spring-security-and-threads, covering not only @Async but any custom thread – David Canós Jan 03 '19 at 11:00
  • @hitch.united See this other answer: https://stackoverflow.com/a/65353568/1026624 – marcioggs Dec 18 '20 at 08:07
5

A little bit another solution, like @viator write:

<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" value="MODE_INHERITABLETHREADLOCAL" />
    </bean>

Working like a charm.

Taras Melon
  • 377
  • 4
  • 16
5

Via Java configuration without reflection.

import javax.annotation.PostConstruct;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContextHolder;

@Configuration
public class SecurityConfig {

  @PostConstruct
  public void enableAuthCtxOnSpawnedThreads() {
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
  }
}
marcioggs
  • 648
  • 11
  • 23
  • Used this, but I got burned with `CompletableFuture` because Security context was null and didn't realize it before it was too late. – Manuel Pap Apr 01 '22 at 19:26