Servlet 3 Async Support with Spring MVC and Spring Security – 使用Spring MVC和Spring Security的Servlet 3异步支持

最后修改: 2017年 1月 2日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

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.

在这个快速教程中,我们将重点介绍Servlet 3对异步请求的支持,以及Spring MVC和Spring Security如何处理这些请求

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.

网络应用中异步性的最基本动机是处理长期运行的请求。在大多数用例中,我们需要确保Spring Security本金被传播给这些线程。

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

当然,Spring Security 在MVC的范围之外与@Async集成,也可以处理HTTP请求。

2. Maven Dependencies

2.Maven的依赖性

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

为了在Spring MVC中使用异步集成,我们需要在pom.xml中包含以下依赖项。

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

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

最新版本的 Spring Security 依赖项可以在这里找到。

3. Spring MVC and @Async

3.Spring MVC和@Async

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

根据官方的文档,Spring Security与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:

第一步是确保我们的springSecurityFilterChain被设置为处理异步请求。我们可以在Java配置中完成,在我们的Servletconfig类中添加以下一行。

dispatcher.setAsyncSupported(true);

or in XML config:

或在XML配置中。

<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参数。

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

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

现在我们已经准备好发送带有SecurityContext传播的异步请求。

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.

Spring Security的内部机制将确保在另一个Thread中提交响应导致用户注销时,我们的SecurityContext不再被清空。

4. Use Cases

4.使用案例

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

让我们通过一个简单的例子来看看这一点的作用。

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

    return new Callable<Boolean>() {
        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.

我们想检查Spring的SecurityContext是否被传播到了新的线程。

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

上面介绍的方法将自动使其Callable在包含SecurityContext的情况下被执行,如日志所示。

web - 2017-01-02 10:42:19,011 [http-nio-8081-exec-3] INFO
  o.baeldung.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.baeldung.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.

如果不设置要传播的SecurityContext,第二个请求将以null值告终。

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

还有其他重要的用例,可以使用带有传播的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

5.结论

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

在这个简短的教程中,我们说明了Spring支持在认证的上下文中处理异步请求

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.

这个例子也可以作为一个Maven项目在Github上提供。