I've a Spring Boot 2.2 application. I created a service like this:
@Async
@PreAuthorize("hasAnyRole('ROLE_PBX')")
@PlanAuthorization(allowedPlans = {PlanType.BUSINESS, PlanType.ENTERPRISE})
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
    log.debug("Current tenant {}", TenantContext.getCurrentTenantId());  
    return new AsyncResult<AuditCdr>(auditCdrRepository.save(cdr3CXMapper.cdr3CXDtoToAuditCdr(cdrRecord)));
}
this is my @Async configuration:
@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("threadAsync");
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;
    }
}
Using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL I see the Security context is passed to the @Async method.
In my multi-tenant application I use a ThreadLocal to set the tenant's id:
public class TenantContext {
    public final static String TENANT_DEFAULT = "empty";
    private static final ThreadLocal<String> code = new ThreadLocal<>();
    public static void setCurrentTenantId(String code) {
        if (code != null)
            TenantContext.code.set(code);
    }
    public static String getCurrentTenantId() {
        String tenantId = code.get();
        if (StringUtils.isNotBlank(tenantId)) {
            return tenantId;
        }
        return TENANT_DEFAULT;
    }
    public static void clear() {
        code.remove();
    }
}
Because ThreadLocal is related to the thread, it's not available in the @Async method. Furthemore my custom @PlanAuthorization aop needs it to perform verifications of the tenant's plan.
Is there a clean way to set TenantContext in any @Async method in my application?
 
     
     
     
     
    
code = ThreadLocal.withInitial(() -> TENANT_DEFAULT);`– M. Prokhorov May 08 '20 at 11:14