Application is loosing Spring authentication context in Threads

16 views Asked by At

While updating settings I want to run a thread that updates some extra things in the background (for which the user shouldn't wait). It used to work with Spring4 but cannot make it work in Spring5 (JDK17). I know I'm probably missing something silly I just cannot see it ...

Basically I want to call this as Async:

tenantUpdateHandler.update(tenantId, settings);

The call is wrapped in a Runnable class and started like so:

UpdateTenantThread updateTenantThread = applicationCtx.getBean(UpdateTenantThread.class);
updateTenantThread.init(tenantId, libraryId, settings);
Thread updateThread = new Thread(updateTenantThread);
updateThread.start();

The exception I get after starting the thread:

Exception in thread "Thread-27" org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:350)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:214)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
    at com.app.saas.service.TenantService$$EnhancerBySpringCGLIB$$9d387e3f.update(<generated>)
    at com.app.saas.service.TenantUpdateHandlerImpl.update(TenantUpdateHandlerImpl.java:44)
    at com.app.core.service.UpdateTenantThread.run(UpdateTenantThread.java:47)
    at java.base/java.lang.Thread.run(Thread.java:833)

The TenantService from the stack is secured (@Service, @Secured({Role.ADMIN}))

I have enabled thread inheritance for Spring Security like so:

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

This actually used to be just a private subclass, but I changed it to a Component to make sure it is managed by Spring.

/**
 * Update tenant data for Thread.
 */
@Component
@Scope("prototype")
public class UpdateTenantThread implements Runnable {
    private static final Logger LOG = LoggerFactory.getLogger(UpdateTenantThread.class);

    Long tenantId;
    Properties settings;

    @Autowired
    private TenantUpdateHandler tenantUpdateHandler;

    UpdateTenantCopy() {}

    public void init(Long tenantId,
        Properties settings) {
        this.tenantId = tenantId;
        this.settings = (Properties) settings.clone();
    }

    @Override
    public void run() {
        try {
            tenantUpdateHandler.update(tenantId, settings);
        } catch (TenantServiceException ex) {
            LOG.error("Problem updating copy of settings", ex);
        }
    }
}
0

There are 0 answers