1. 概述
在本簡短教程中,我們將學習如何解決“預檢請求的響應具有無效的 HTTP 狀態碼 401”錯誤,該錯誤可能出現在支持跨域通信並使用 Spring Security 的應用程序中。
首先,我們將瞭解跨域請求是什麼,然後我們將修復一個問題示例。
2. 跨域請求
跨域請求,簡而言之,是指請求的origin和目標域不同的一系列HTTP請求。例如,當一個Web應用程序託管在一個域中,而瀏覽器向另一個域中的服務器發送AJAX請求時,就會發生這種情況。
為了管理跨域請求,服務器需要啓用一種稱為CORS(跨域資源共享)的機制。
CORS的第一步是發送一個<em>OPTIONS</em>請求,以確定目標域是否支持它。<strong>這被稱為預檢請求。</strong>
服務器可以響應預檢請求,並提供一系列的響應頭:
- Access-Control-Allow-Origin: 定義允許訪問資源的origin。 ‘*’ 表示任何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 僅從其參數中指定的源訪問。
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 命令,因此我們不會在輸出中看到“Response for preflight has invalid HTTP status code 401” 錯誤信息。
但是,我們可以通過創建一個從不同域運行的 front end 應用程序並在瀏覽器中運行它來重現這個確切的錯誤。
6. 解決方案
我們沒有在我們的 Spring Security 配置中明確排除授權的預檢請求。請記住,Spring Security 默認情況下通過所有 進行安全保護。
因此,
Spring 提供了一種內置的解決方案,用於排除 OPTIONS 請求的授權檢查:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.cors(Customizer.withDefaults()); // 禁用此行以重現 CORS 401
return http.build();
}
}
方法會將 Spring 提供的 添加到應用程序上下文中,從而繞過 OPTIONS 請求的授權檢查。
現在我們可以再次測試我們的應用程序,並看到它正在正常工作。
7. 結論
在本文中,我們學習瞭如何修復“預檢請求的響應具有無效的 HTTP 狀態碼 401”錯誤,該錯誤與 Spring Security 和跨域請求相關。
請注意,通過示例,客户端和 API 應該在不同的域名或端口上運行,才能重現該問題。例如,在本地機器上運行時,可以將默認主機名映射到客户端,並將機器 IP 地址映射到我們的 REST API。