1. 簡介
在本快速教程中,我們將重點介紹 Servlet 3 對異步請求的支持,以及 Spring MVC 和 Spring Security 如何處理這些請求。異步請求的最基本動機是在 Web 應用程序中處理長時間運行的請求。在大多數用例中,我們需要確保 Spring Security 主體能夠傳遞到這些線程。
當然,Spring Security 與 @Async 集成,不在 MVC 的範圍內,也不作為 HTTP 請求進行處理。
2. Maven 依賴項
為了使用 Spring MVC 中的異步集成,我們需要將以下依賴項包含到我們的 pom.xml 中:
<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 和 @Async
根據官方 文檔,Spring Security 與 WebAsyncManager 集成。
第一步是確保我們的 springSecurityFilterChain 已配置為處理異步請求。我們可以通過在 Java 配置中添加以下行來完成,添加到我們的 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 是否傳播到新線程。
上面呈現的方法將自動執行其 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
如果沒有設置 SecurityContext 傳播到,第二個請求將以 null 值結束。
還有其他重要的使用案例,用於使用異步請求,並傳播 SecurityContext:
- 我們想創建多個並行運行並且可能需要大量時間執行的外部請求
- 我們有一些重要的本地處理,並且我們的外部請求可以在該處理並行執行
- 其他代表“點並忘記”場景,例如發送電子郵件
請注意,如果我們的多個方法調用以前以同步方式鏈接在一起,將這些轉換為異步方法可能需要同步結果。
5. 結論
在本教程中,我們演示了在身份驗證上下文中處理異步請求的 Spring 支持。
從編程模型角度來看,這些新功能乍一看似乎很簡單。但確實有一些方面需要更深入的理解。