If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Introduction
In this tutorial, we are going to focus on the propagation of the Spring Security principal with @Async.
By default, the Spring Security Authentication is bound to a ThreadLocal β so, when the execution flow runs in a new thread with @Async, thatβs not going to be an authenticated context.
Thatβs not ideal β letβs fix it.
2. Maven Dependencies
In order to use the async integration in Spring Security, we need to include the following section in the dependencies of our pom.xml:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>6.1.5</version>
</dependency>
The latest version of Spring Security dependencies can be found here.
3. Spring Security Propagation With @Async
Letβs first write a simple example:
@RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody
public Object standardProcessing() throws Exception {
log.info("Outside the @Async logic - before the async call: "
+ SecurityContextHolder.getContext().getAuthentication().getPrincipal());
asyncService.asyncCall();
log.info("Inside the @Async logic - after the async call: "
+ SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
We want to check if the Spring SecurityContext is propagated to the new thread. First, we log the context before the async call, next we run asynchronous method and finally we log the context again. The asyncCall() method has the following implementation:
@Async
@Override
public void asyncCall() {
log.info("Inside the @Async logic: "
+ SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
As we can see, itβs only one line of code that will output the context inside the new thread of asynchronous method.
4. The Default Configuration
By default, the security context inside the @Async method will have a null value.
In particular, if weβll run the async logic, weβll be able to log the Authentication object in the main program, but when weβll log it inside the @Async, itβs going to be null. This is an example logs output:
web - 2016-12-30 22:41:58,916 [http-nio-8081-exec-3] INFO
o.baeldung.web.service.AsyncService -
Outside the @Async logic - before the async call:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; ...
web - 2016-12-30 22:41:58,921 [http-nio-8081-exec-3] INFO
o.baeldung.web.service.AsyncService -
Inside the @Async logic - after the async call:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; ...
web - 2016-12-30 22:41:58,926 [SimpleAsyncTaskExecutor-1] ERROR
o.s.a.i.SimpleAsyncUncaughtExceptionHandler -
Unexpected error occurred invoking async method
'public void com.baeldung.web.service.AsyncServiceImpl.asyncCall()'.
java.lang.NullPointerException: null
So, as you can see, inside the executor thread, our call fails with a NPE, as expected β because the Principal isnβt available there.
5. Async Security Context Configuration
If we want to have access to the principal inside the async thread, just as we have access to it outside, weβll need to create the DelegatingSecurityContextAsyncTaskExecutor bean:
@Bean
public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(ThreadPoolTaskExecutor delegate) {
return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
}
By doing so, Spring will use the current SecurityContext inside each @Async call.
Now, letβs run the application again and have a look at the logging information to make sure thatβs the case:
web - 2016-12-30 22:45:18,013 [http-nio-8081-exec-3] INFO
o.baeldung.web.service.AsyncService -
Outside the @Async logic - before the async call:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; ...
web - 2016-12-30 22:45:18,018 [http-nio-8081-exec-3] INFO
o.baeldung.web.service.AsyncService -
Inside the @Async logic - after the async call:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; ...
web - 2016-12-30 22:45:18,019 [SimpleAsyncTaskExecutor-1] INFO
o.baeldung.web.service.AsyncService -
Inside the @Async logic:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; ...
And here we are β just as we expected, weβre seeing the same principal inside the async executor thread.
6. Use Cases
There are a few interesting use cases where we might want to make sure the SecurityContext gets propagated like this:
- we want to make multiple external requests which can run in parallel and which may take significant time to execute
- we have some significant processing to do locally and our external request can execute in parallel to that
- other represent fire-and-forget scenarios, like for example sending an email
7. Conclusion
In this quick tutorial, we presented the Spring support for sending asynchronous requests with propagated SecurityContext. From a programming model perspective, the new capabilities appear deceptively simple.
Please note, that if multiple method calls were previously chained together in a synchronous fashion, converting to an asynchronous approach may require synchronizing results.
