1. 引言
在本快速教程中,我們將重點介紹 Servlet 3 對異步請求的支持,以及 Spring MVC 和 Spring Security 如何處理這些請求。
異步請求在 Web 應用程序中最基本的原因是處理長時間運行的請求。 在大多數用例中,我們需要確保 Spring Security 的 principal 被傳遞到這些線程中。
當然,Spring Security 與 @Async 之外的集成也涵蓋了在 MVC 之外和處理 HTTP 請求的場景。
2. Maven 依賴
為了使用 Spring MVC 中的異步集成,我們需要將以下依賴項包含到我們的 <em pom.xml</em> 中:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>6.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>6.1.5</version>
</dependency>
Spring Security 最新版本的依賴項可以在這裏找到:此處。
3. Spring MVC 和 <em @Async>
根據官方文檔 文檔,Spring Security 與 WebAsyncManager 集成。
第一步是確保我們的 <em springSecurityFilterChain> 能夠處理異步請求。我們可以通過在 Java 配置中,將以下行添加到我們的 <i Servlet 配置類中來實現:
dispatcher.setAsyncSupported(true);或者在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>我們還需要在servlet配置中啓用async-supported參數:
<servlet>
...
<async-supported>true</async-supported>
...
</servlet>現在,我們準備好使用帶有 SecurityContext 的異步請求。
Spring Security 內部機制將確保在另一個 Thread 中提交響應時,不會清除我們的 SecurityContext,從而避免用户註銷。
4. 用例
讓我們通過一個簡單的示例來演示一下:
@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;
}
};
}我們想檢查 Spring 的 SecurityContext 是否被傳遞到新的線程。
上面提供的該方法會自動使用包含 SecurityContext 的 Callable 執行,如日誌所示:
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在未設置 SecurityContext 傳播時,第二個請求將最終得到 null 值。
使用傳播的 SecurityContext 進行異步請求還有其他重要的用例:
- 我們希望創建多個可以並行運行且可能需要大量時間執行的外部請求
- 我們本地需要進行一些重要的處理,並且外部請求可以與這些處理並行執行
- 其他“發送並忘記”場景,例如發送電子郵件
請注意,如果之前的多個方法調用以同步方式鏈接在一起,轉換為異步方法可能需要同步結果。
5. 結論
在本教程中,我們演示了 Spring 支持在認證上下文中處理異步請求。
從編程模型角度來看,這些新功能乍一看似乎很簡單。但確實有一些方面需要更深入的理解。