1. 概述
在任何現代瀏覽器中,跨源資源共享 (CORS) 規範與 HTML5 和通過 REST API 消費數據的 JS 客户端的出現而相關。
通常,提供 JS 的主機(例如 example.com)與提供數據的主機(例如 api.example.com)不同。在這種情況下,CORS 允許跨域通信。
Spring 提供了對 CORS 的一流 支持,提供了一種簡單而強大的配置方法,可用於在任何 Spring 或 Spring Boot Web 應用程序中配置 CORS。
2. 控制器方法 CORS 配置
啓用 CORS 非常簡單 — 只需要添加註解 。
我們可以用多種不同的方式來實現它。
2.1. 在帶有 <em @RequestMapping- 註解的 Handler 方法上使用 <em @CrossOrigin
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}在上面的示例中,我們僅啓用了 CORS 對於 retrieve() 方法。我們可以看到,我們沒有為 @CrossOrigin 註解設置任何配置,因此它使用了默認值:
- 所有來源均被允許。
- 允許的 HTTP 方法是 @RequestMapping 註解中指定的(GET,在本示例中)。
- 預檢響應緩存的時間(maxAge)為 30 分鐘。
2.2. 在控制器上使用 <em @CrossOrigin
@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}本次,我們已在類級別添加了 @CrossOrigin 註解。因此,retrieve() 和 remove() 方法均已啓用。可以通過指定註解屬性的值(例如 origins、methods、allowedHeaders、exposedHeaders、allowCredentials 或 maxAge)來自定義配置。
2.3. @CrossOrigin on Controller and Handler Method
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://example.com")
@RequestMapping(method = RequestMethod.GET, "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}Spring 將會結合兩個註解的屬性以創建合併的 CORS 配置。
在這裏,兩個方法都將具有 3,600 秒的 maxAge,remove() 方法允許所有來源,而retrieve() 方法僅允許來自 http://example.com 的來源。
3. 全局 CORS 配置
作為一種替代方案,Spring 允許我們通過控制器定義全局 CORS 配置。這類似於使用基於 Filter 的解決方案,但可以在 Spring MVC 中聲明,並與精細的 @CrossOrigin 配置結合使用。
默認情況下,允許所有源頭和 GET、HEAD 和 POST 方法。
3.1. JavaConfig
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}上述示例允許從任何源頭嚮應用程序中的任何端點發起 CORS 請求。
為了進一步限制,registry.addMapping 方法返回一個 CorsRegistration 對象,我們可以利用它進行額外的配置。 此外,還有一個 allowedOrigins 方法,允許我們指定允許的源列表。 這在我們需要在運行時從外部源加載此數組時非常有用。
此外,還有 allowedMethods、allowedHeaders、exposedHeaders、maxAge 和 allowCredentials 等方法,可用於設置響應頭和自定義選項。 例如,我們可以通過將 allowedMethods(“*”) 添加到上述配置中,來允許任何 HTTP 方法通過 CORS。
值得注意的是,自 2.4.0 版本以來,Spring Boot 引入了 allowedOriginPatterns,除了 allowedOrigins,提供了更大的靈活性。 此外,當 allowCredentials 為 true 時,allowedOrigins 不能包含特殊值 ‘*’,因為這無法設置在 Access-Control-Allow-Origin 響應頭中。 為了解決此問題並允許憑據設置為一組源,我們可以顯式列出它們,或者考慮使用 allowedOriginPatterns。
3.2. XML 命名空間
這段最小的 XML 配置允許在 路徑模式上啓用 CORS,並具有與 JavaConfig 一樣默認的屬性:
<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>當然,以下是翻譯後的內容:
還可以聲明多個 CORS 映射,並自定義其屬性:
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://domain1.com, http://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="false"
max-age="123" />
<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />
</mvc:cors>4. 使用 Spring Security 進行 CORS 處理
如果我們在項目中使用了 Spring Security,則必須採取額外的步驟以確保其與 CORS 兼容。這是因為 CORS 需要在先進行處理。否則,Spring Security 會在請求到達 Spring MVC 之前拒絕該請求。
幸運的是,Spring Security 提供了內置解決方案:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and()...
}
}本文檔將更詳細地解釋相關內容。
我們可以配置 CORS 以覆蓋默認的 Spring Security CORS 處理。為此,我們需要添加一個 CorsConfigurationSource Bean,該 Bean 使用 CorsConfiguration 實例來處理 CORS 配置。 http.cors() 方法如果添加了 corsFilter Bean,則使用 CorsFilter,否則使用 CorsConfigurationSource。如果兩者都沒有配置,則使用 Spring MVC 模式檢查器處理程序。
讓我們在 WebSecurityConfig 類中添加 CorsConfigurationSource Bean:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}在這裏,我們使用默認構造函數創建一個 CorsConfiguration 實例,然後設置允許的 origin、允許的方法和響應頭。上述配置允許來自任何 origin、任何方法和任何頭部的請求到達應用程序中的任何 endpoint。最後,我們將它作為參數傳遞給 UrlBasedCorsConfigurationSource 實例,並返回它。
5. 工作原理
CORS 請求會自動分發到註冊的 HandlerMappings
它們處理 CORS 預檢請求並使用 CorsProcessor 實現(默認情況下是 DefaultCorsProcessor)來添加相關的 CORS 響應頭(例如 Access-Control-Allow-Origin)。
CorsConfiguration 允許我們指定 CORS 請求的處理方式,包括允許的源、頭和方法等。我們可以通過多種方式提供它:
- AbstractHandlerMapping#setCorsConfiguration() 允許我們指定一個 Map,其中包含多個 CorsConfiguration,並映射到路徑模式,例如 /api/**。
- 子類可以通過重寫 AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest) 方法提供自己的 CorsConfiguration。
- 處理器可以實現 CorsConfigurationSource 接口(例如 ResourceHttpRequestHandler 現在正在執行此操作)以為每個請求提供 CorsConfiguration。
6. 結論
在本文中,我們展示了 Spring 如何提供支持,以便在我們的應用程序中啓用 CORS。
我們首先從控制器配置開始。我們瞭解到,只需在特定方法或整個控制器中添加註解 @CrossOrigin 即可啓用 CORS。
此外,我們還了解到,為了在控制器之外控制 CORS 配置,可以使用 JavaConfig 或 XML 文件進行平滑配置。