跳到主要内容

CORS

ChatGPT-4o 中英对照 CORS

Spring MVC 允许你处理 CORS(跨域资源共享)。本节将介绍如何实现这一点。

介绍

出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。例如,你可能在一个标签页中打开你的银行账户,而在另一个标签页中打开 evil.com。evil.com 的脚本不应该能够使用你的凭据向你的银行 API 发起 AJAX 请求——例如,从你的账户中提取资金!

跨域资源共享(CORS)是一个由 W3C 规范 实现的功能,大多数浏览器 都支持该功能,它允许你指定哪些类型的跨域请求是被授权的,而不是使用基于 IFRAME 或 JSONP 的不太安全和功能较弱的变通方法。

凭证请求

使用带凭证的 CORS 请求需要启用 allowedCredentials。请注意,此选项会与配置的域建立高度信任,同时也会通过暴露诸如 cookies 和 CSRF 令牌等敏感的用户特定信息,增加 web 应用程序的攻击面。

启用凭据还会影响配置的 "*" CORS 通配符的处理方式:

  • allowOrigins 中不允许使用通配符,但可以使用 allowOriginPatterns 属性来匹配一组动态的来源。

  • 当设置在 allowedHeadersallowedMethods 上时,Access-Control-Allow-HeadersAccess-Control-Allow-Methods 响应头通过复制 CORS 预检请求中指定的相关头和方法来处理。

  • 当设置在 exposedHeaders 上时,Access-Control-Expose-Headers 响应头要么被设置为配置的头列表,要么被设置为通配符。虽然 CORS 规范在 Access-Control-Allow-Credentials 设置为 true 时不允许使用通配符,但大多数浏览器支持它,并且在 CORS 处理期间并非所有响应头都可用,因此无论 allowCredentials 属性的值如何,通配符都是指定时使用的头值。

注意

虽然这样的通配符配置可能很方便,但建议尽可能配置一组有限的值,以提供更高的安全性。

处理

CORS 规范区分了预检请求、简单请求和实际请求。要了解 CORS 的工作原理,你可以阅读这篇文章,或查看规范以获取更多详细信息。

Spring MVC HandlerMapping 实现为 CORS 提供了内置支持。在成功将请求映射到处理器之后,HandlerMapping 实现会检查给定请求和处理器的 CORS 配置并采取进一步的操作。预检请求直接处理,而简单和实际的 CORS 请求则被拦截、验证,并设置所需的 CORS 响应头。

为了启用跨域请求(即 Origin 头存在且与请求的主机不同),您需要有一些明确声明的 CORS 配置。如果没有找到匹配的 CORS 配置,预检请求将被拒绝。对于简单和实际的 CORS 请求,响应中不会添加 CORS 头,因此浏览器会拒绝它们。

每个 HandlerMapping 都可以通过基于 URL 模式的 CorsConfiguration 映射单独配置。在大多数情况下,应用程序使用 MVC Java 配置或 XML 命名空间来声明此类映射,这会导致一个全局映射被传递给所有 HandlerMapping 实例。

您可以在 HandlerMapping 级别结合全局 CORS 配置与更细粒度的处理器级别 CORS 配置。例如,带注解的控制器可以使用类级或方法级的 @CrossOrigin 注解(其他处理器可以实现 CorsConfigurationSource)。

全局和本地配置组合的规则通常是累加的——例如,所有全局和所有本地的来源。对于那些只能接受单一值的属性,例如,allowCredentialsmaxAge,本地值会覆盖全局值。有关更多详细信息,请参见 CorsConfiguration#combine(CorsConfiguration)

提示

要从源代码中学习更多或进行高级自定义,请查看以下代码:

  • CorsConfiguration

  • CorsProcessorDefaultCorsProcessor

  • AbstractHandlerMapping

@CrossOrigin

@CrossOrigin 注解允许在被注解的控制器方法上进行跨域请求,如下例所示:

@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
java

默认情况下,@CrossOrigin 允许:

  • 所有来源。

  • 所有头信息。

  • 映射到控制器方法的所有 HTTP 方法。

allowCredentials 默认未启用,因为它建立了一个信任级别,会暴露敏感的用户特定信息(如 cookies 和 CSRF 令牌),因此应仅在适当的情况下使用。当启用时,allowOrigins 必须设置为一个或多个特定域(但不能是特殊值 "*"),或者可以使用 allowOriginPatterns 属性来匹配一组动态的来源。

maxAge 被设置为 30 分钟。

@CrossOrigin 也支持在类级别使用,并且会被所有方法继承,如下例所示:

@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) {
// ...
}
}
java

您可以在类级别和方法级别使用 @CrossOrigin,如下例所示:

@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) {
// ...
}
}
java

全局配置

除了细粒度的控制器方法级配置之外,您可能还希望定义一些全局的 CORS 配置。您可以在任何 HandlerMapping 上单独设置基于 URL 的 CorsConfiguration 映射。然而,大多数应用程序使用 MVC Java 配置或 MVC XML 命名空间来实现这一点。

默认情况下,全局配置启用以下功能:

  • 所有来源。

  • 所有头信息。

  • GETHEADPOST 方法。

allowCredentials 默认未启用,因为这会建立一个信任级别,暴露敏感的用户特定信息(如 cookies 和 CSRF 令牌),应仅在适当的情况下使用。当启用时,必须将 allowOrigins 设置为一个或多个特定域(但不能是特殊值 "*"),或者可以使用 allowOriginPatterns 属性来匹配一组动态的来源。

maxAge 被设置为 30 分钟。

Java 配置

要在 MVC Java 配置中启用 CORS,您可以使用 CorsRegistry 回调,如以下示例所示:

@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...
}
}
java

XML 配置

要在 XML 命名空间中启用 CORS,可以使用 <mvc:cors> 元素,如下例所示:

<mvc:cors>

<mvc:mapping path="/api/**"
allowed-origins="https://domain1.com, https://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="true"
max-age="123" />

<mvc:mapping path="/resources/**"
allowed-origins="https://domain1.com" />

</mvc:cors>
xml

CORS 过滤器

您可以通过内置的 CorsFilter 应用 CORS 支持。

备注

如果你尝试在 Spring Security 中使用 CorsFilter,请记住 Spring Security 对 CORS 有内置支持

要配置过滤器,将 CorsConfigurationSource 传递给其构造函数,如以下示例所示:

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);
java