--- title: Spring MVC 之跨域 date: 2023-02-16 20:33:26 order: 05 categories: - Java - 框架 - Spring - SpringWeb tags: - Java - 框架 - Spring - Web - CORS permalink: /pages/a7b9b8e7/ --- # Spring MVC 之跨域 Spring MVC 支持跨域处理(CORS)。 ## 简介 出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。例如,可以在一个选项卡中使用您的银行帐户,而在另一个选项卡中使用 evil.com。来自 evil.com 的脚本不应该能够使用您的凭据向您的银行 API 发出 AJAX 请求——例如从您的账户中取款! 跨域(CORS)是由 [大多数浏览器](https://caniuse.com/#feat=cors) 实施的 [W3C 规范](https://www.w3.org/TR/cors/),可让您指定哪种跨域请求是授权,而不是使用基于 IFRAME 或 JSONP 的不太安全和不太强大的解决方法。 ## 处理 CORS 规范分为预检请求、简单请求和实际请求。要了解 CORS 的工作原理,可以阅读 [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) 等,或者查看规范了解更多详细信息。 Spring MVC `HandlerMapping` 实现提供了对 CORS 的内置支持。成功将请求映射到处理程序后,`HandlerMapping` 实现检查给定请求和处理程序的 CORS 配置并采取进一步的操作。预检请求被直接处理,而简单和实际的 CORS 请求被拦截、验证,并设置了所需的 CORS 响应标头。 为了启用跨源请求(即存在 `Origin` 标头并且与请求的主机不同),您需要有一些明确声明的 CORS 配置。如果未找到匹配的 CORS 配置,预检请求将被拒绝。没有 CORS 标头添加到简单和实际 CORS 请求的响应中,因此浏览器会拒绝它们。 每个 `HandlerMapping` 都可以[配置](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations- java.util.Map-) 单独使用基于 URL 模式的 `CorsConfiguration` 映射。 在大多数情况下,应用程序使用 MVC Java 配置或 XML 命名空间来声明此类映射,这会导致将单个全局映射传递给所有 `HandlerMapping` 实例。 可以将 `HandlerMapping` 级别的全局 CORS 配置与更细粒度的处理程序级别的 CORS 配置相结合。 例如,带注释的控制器可以使用类级或方法级的 `@CrossOrigin` 注释(其他处理程序可以实现 `CorsConfigurationSource`)。 The rules for combining global and local configuration are generally additive — for example, all global and all local origins. For those attributes where only a single value can be accepted, e.g. `allowCredentials` and `maxAge`, the local overrides the global value. 结合全局和局部配置的规则通常是附加的⟩——例如,所有全局和所有局部起源。 对于那些只能接受单个值的属性,例如 `allowCredentials` 和 `maxAge`,局部覆盖全局值。 ## `@CrossOrigin` [`@CrossOrigin`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html) 注解在带注解的 Controller 方法上启用跨源请求,如以下示例所示: ```java @RestController @RequestMapping("/account") public class AccountController { @CrossOrigin @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { // ... } } ``` 默认,`@CrossOrigin` 允许访问: - 所以 origin - 所以 header - 所以 Controller 方法映射到的 HTTP 方法 `allowCredentials` 默认情况下不启用,因为它建立了一个信任级别,可以公开敏感的用户特定信息(例如 cookie 和 CSRF 令牌),并且只应在适当的情况下使用。启用时,必须将 `allowOrigins` 设置为一个或多个特定域(但不是特殊值 `"*"`),或者 `allowOriginPatterns` 属性可用于匹配一组动态来源。 `maxAge` 单位为分钟 `@CrossOrigin` 也支持类级别,并且被所有方继承,如下所示: ```java @CrossOrigin(origins = "https://domain2.com", maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { // ... } } ``` 可以同时在类级别和方法级别上使用 `@CrossOrigin` ```java @CrossOrigin(maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { @CrossOrigin("https://domain2.com") @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { // ... } } ``` ## 全局配置 除了细粒度的控制器方法级别配置之外,您可能还想定义一些全局 CORS 配置。您可以在任何 `HandlerMapping` 上单独设置基于 URL 的 `CorsConfiguration` 映射。但是,大多数应用程序使用 MVC Java 配置或 MVC XML 命名空间来执行此操作。 默认情况下,全局配置启用以下功能: - 所以 origin - 所以 header - `GET`、`HEAD` 和 `POST` 方法 `allowCredentials` 默认情况下不启用,因为它建立了一个信任级别,可以公开敏感的用户特定信息(例如 cookie 和 CSRF 令牌),并且只应在适当的情况下使用。启用时,必须将 `allowOrigins` 设置为一个或多个特定域(但不是特殊值 `"*"`),或者 `allowOriginPatterns` 属性可用于匹配一组动态来源。 `maxAge` 单位为分钟 ### Java 配置 要在 MVC Java 配置中启用 CORS,您可以使用 `CorsRegistry` 回调,如以下示例所示: ```java @Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://domain2.com") .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") .allowCredentials(true).maxAge(3600); // Add more mappings... } } ``` ### XML 配置 要在 XML 命名空间中启用 CORS,可以使用 `` 元素,如以下示例所示: ```xml ``` ## CORS 过滤器 可以通过 Spring 内置的 [`CorsFilter`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/filter/CorsFilter.html) 支持 CORS。 要配置过滤器,请将 `CorsConfigurationSource` 传递给它的构造函数,如以下示例所示: ```java CorsConfiguration config = new CorsConfiguration(); // Possibly... // config.applyPermitDefaultValues() config.setAllowCredentials(true); config.addAllowedOrigin("https://domain1.com"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); CorsFilter filter = new CorsFilter(source); ``` ## 参考资料 - [Spring Framework 官方文档](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/index.html) - [Spring Framework 官方文档之 Web](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html)