1. 概述
在本簡短教程中,我們將學習如何解決“預檢請求的響應具有無效的 HTTP 狀態碼 401”錯誤,該錯誤可能出現在支持跨域通信且使用 Spring Security 的應用程序中。
首先,我們將瞭解跨域請求的概念,然後我們將修復一個問題示例。
2. 跨域請求
跨域請求,簡而言之,是指請求的源(origin)和目標(target)不同的HTTP請求。例如,當一個Web應用程序託管在一個域名下,瀏覽器向另一個域名下的服務器發送AJAX請求時,這種情況就屬於跨域請求。
為了管理跨域請求,服務器需要啓用一種機制,稱為CORS(跨域資源共享)。
CORS的第一步是發送一個OPTIONS請求,以確定目標請求是否支持它。這被稱為預檢請求。
服務器可以響應預檢請求,並返回一組頭部信息:
- Access-Control-Allow-Origin: 定義允許訪問資源的源。“*”表示任何源
- Access-Control-Allow-Methods: 指出允許的跨域請求HTTP方法
- Access-Control-Allow-Headers: 指出允許的跨域請求頭部
- Access-Control-Max-Age: 定義預檢請求結果的過期時間
因此,如果預檢請求不滿足從這些響應頭部確定的條件,則實際的跟進請求將會拋出與跨域請求相關的錯誤。
在我們的Spring驅動服務中添加CORS支持很容易,但如果配置不當,這個預檢請求將會始終返回401錯誤。
3. 創建支持 CORS 的 REST API
為了模擬問題,首先創建一個支持跨域請求的簡單 REST API:
@RestController
@CrossOrigin("http://localhost:4200")
public class ResourceController {
@GetMapping("/user")
public String user(Principal principal) {
return principal.getName();
}
}@CrossOrigin 註解確保我們的API僅從其參數中指定的源(origin)訪問。
4. 保護我們的 REST API
現在,讓我們使用 Spring Security 來保護我們的 REST API:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}在配置類中,我們強制執行了所有傳入請求的授權。因此,它將拒絕所有缺少有效授權令牌的請求。
5. 發送預檢請求
現在我們已經創建了 REST API,讓我們使用 curl 嘗試發送預檢請求:
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200"
-X OPTIONS http://localhost:8080/user
...
< HTTP/1.1 401
...
< WWW-Authenticate: Basic realm="Realm"
...
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Credentials: true
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
...從該命令的輸出中可以看出,請求被拒絕,狀態碼為 401。
由於這是一個 curl 命令,因此我們不會在輸出中看到“預檢請求的響應具有無效的 HTTP 狀態碼 401”錯誤。
但是,通過創建一個從不同域消耗我們 REST API 的前端應用程序並在瀏覽器中運行它,我們可以重現這個確切的錯誤。
6. 解決方案
我們沒有在我們的 Spring Security 配置中明確排除授權預檢請求。請記住,Spring Security 默認情況下通過所有端點進行安全保護。
因此,我們的 API 期望在 OPTIONS 請求中包含授權令牌。
Spring 提供了內置的解決方案,用於排除 OPTIONS 請求的授權檢查:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.cors(Customizer.withDefaults()); // disable this line to reproduce the CORS 401
return http.build();
}
}cors() 方法將 Spring 提供的 CorsFilter 添加到應用程序上下文中,從而繞過 OPTIONS 請求的授權檢查。
現在我們可以再次測試我們的應用程序,並確認它正在正常工作。
7. 結論
在本文中,我們學習瞭如何修復“預檢請求的響應具有無效的 HTTP 狀態碼 401”錯誤,該錯誤與 Spring Security 和跨域請求相關。
請注意,通過示例,客户端和 API 應該在不同的域名或端口上運行,才能重現該問題。 例如,在本地機器上運行時,可以將默認主機名映射到客户端,並將機器 IP 地址映射到我們的 REST API。