Servlet 3 Async Support with Spring MVC and Spring Security

1. Introduction

In this quick tutorial, we’re going to focus on the Servlet 3 support for async requests, and how Spring MVC and Spring Security handle these.

The most basic motivation for asynchronicity in web applications is to handle long running requests. In most use cases, we’ll need to make sure the Spring Security principal is propagated to these threads.

And, of course, Spring Security integrates with @Async outside the scope of MVC and processing HTTP requests as well.

2. Maven Dependencies

In order to use the async integration in Spring MVC, we need to include the following dependencies into our pom.xml:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.1.RELEASE</version>
</dependency>

The latest version of Spring Security dependencies can be found here.

3. Spring MVC and @Async

According to the official docs, Spring Security integrates with WebAsyncManager.

The first step is to ensure our springSecurityFilterChain is set up for processing asynchronous requests. We can do it either in Java config, by adding following line to our Servlet config class:

dispatcher.setAsyncSupported(true);

or in XML config:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>

We also need to enable the async-supported parameter in our servlet configuration:

<servlet>
    ...
    <async-supported>true</async-supported>
    ...
</servlet>

Now we are ready to send asynchronous requests with SecurityContext propagated with them.

Internal mechanisms within Spring Security will ensure that our SecurityContext is no longer cleared out when a response is committed in another Thread resulting in a user logout.

4. Use Cases

Let’s see this in action with a simple example:

@Override
public Callable&lt;Boolean&gt; checkIfPrincipalPropagated() {
    Object before 
      = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    log.info("Before new thread: " + before);

    return new Callable&lt;Boolean&gt;() {
        public Boolean call() throws Exception {
            Object after 
              = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            log.info("New thread: " + after);
            return before == after;
        }
    };
}

We want to check if the Spring SecurityContext is propagated to the new thread.

The method presented above will automatically have its Callable executed with the SecurityContext included, as seen in logs:

web - 2017-01-02 10:42:19,011 [http-nio-8081-exec-3] INFO
  o.maixuanviet.web.service.AsyncService - Before new thread:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN

web - 2017-01-02 10:42:19,020 [MvcAsync1] INFO
  o.maixuanviet.web.service.AsyncService - New thread:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN

Without setting up the SecurityContext to be propagated, the second request will end up with null value.

There are also other important use cases to use asynchronous requests with propagated SecurityContext:

  • 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

Do note that, if our multiple method calls were previously chained together in a synchronous fashion, converting these to an asynchronous approach may require synchronizing results.

5. Conclusion

In this short tutorial, we illustrated the Spring support for processing asynchronous requests in an authenticated context.

From a programming model perspective, the new capabilities appear deceptively simple. But there are certainly some aspects that do require a more in-depth understanding.

This example is also available as a Maven project over on Github.

Related posts:

Java Program to Implement Leftist Heap
Creating a Generic Array in Java
Java 8 and Infinite Streams
Một số tính năng mới về xử lý ngoại lệ trong Java 7
Java Program to Create a Random Linear Extension for a DAG
REST Web service: HTTP Status Code và xử lý ngoại lệ RESTful web service với Jersey 2.x
Jackson JSON Views
Initialize a HashMap in Java
Java – Write to File
Kiểu dữ liệu Ngày Giờ (Date Time) trong java
Notify User of Login From New Device or Location
Java Program to Find the Median of two Sorted Arrays using Binary Search Approach
Intersection of Two Lists in Java
Các chương trình minh họa sử dụng Cấu trúc điều khiển trong Java
Spring Boot - Runners
Custom Thread Pools In Java 8 Parallel Streams
Handle EML file with JavaMail
Tạo ứng dụng Java RESTful Client không sử dụng 3rd party libraries
Java Program to Generate All Pairs of Subsets Whose Union Make the Set
New Features in Java 11
Java Program to Perform String Matching Using String Library
Sử dụng JDBC API thực thi câu lệnh truy vấn dữ liệu
Hướng dẫn Java Design Pattern – Prototype
Introduction to Thread Pools in Java
Lớp Arrarys trong Java (Arrays Utility Class)
Java Streams vs Vavr Streams
How to Remove the Last Character of a String?
Java Program to Use Dynamic Programming to Solve Approximate String Matching
Map Serialization and Deserialization with Jackson
Set Interface trong Java
Java Program to Implement Ternary Heap
Jackson – Unmarshall to Collection/Array