跳到主要内容
版本:7.0.3

CORS

Hunyuan 7b 中英对照 CORS

Spring WebFlux允许你处理CORS(跨源资源共享)。本节将描述如何实现这一点。

介绍

出于安全考虑,浏览器禁止对当前源域之外的资源进行AJAX调用。例如,你可能在一个标签页中查看银行账户信息,在另一个标签页中访问evil.com网站。来自evil.com的脚本不应能够使用你的账户凭据向你的银行API发起AJAX请求——比如从你的账户中取款!

跨源资源共享(CORS)是一种W3C规范大多数浏览器都实现了这一规范。它允许你指定哪些类型的跨域请求是被允许的,而不需要使用基于IFrame或JSONP的、安全性较低且功能有限的解决方案。

处理

CORS规范区分了预检请求(preflight requests)、简单请求(simple requests)和实际请求(actual requests)。要了解CORS的工作原理,你可以阅读这篇文章以及其他相关资料,或者查阅规范以获取更多细节。

Spring WebFlux的HandlerMapping实现提供了对CORS(跨源资源共享)的内置支持。在成功将请求映射到相应的处理程序之后,HandlerMapping会检查该请求和处理程序的CORS配置,并据此采取进一步操作。预检请求(preflight requests)会被直接处理,而普通的CORS请求则会被拦截、验证,并设置所需的CORS响应头。

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

每个HandlerMapping都可以通过基于URL模式的CorsConfiguration映射来单独配置([配置链接:https://docs.spring.io/spring-framework/docs/7.0.3/javadoc-api/org/springframework/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map))。在大多数情况下,应用程序使用WebFlux Java配置来声明此类映射,这样就会得到一个全局的映射,该映射会被传递给所有的HandlerMapping实现。

你可以在HandlerMapping级别进行全局CORS配置,同时也可以进行更细粒度的、在处理程序(handler)级别的CORS配置。例如,带有注解的控制器可以使用类级或方法级的@CrossOrigin注解(其他处理程序可以实现CorsConfigurationSource)。

全局配置和局部配置结合的规则通常是相加的——例如,所有全局配置的属性值与所有局部配置的属性值都会被一起考虑。对于那些只能接受单个值的属性(如 allowCredentialsmaxAge),局部配置的值会覆盖全局配置的值。更多详细信息请参见 CorsConfiguration#combine(CorsConfiguration)

提示

如需从源代码中学习更多内容或进行高级定制,请参阅:

  • CorsConfiguration
  • CorsProcessorDefaultCorsProcessor
  • AbstractHandlerMapping

带凭据的请求

在使用带凭据的请求(credentialed requests)时,需要启用allowedCredentials选项。需要注意的是,此选项会与配置的域名建立较高程度的信任关系,但也会通过暴露用户特定的敏感信息(如cookie和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 属性的值如何,指定的通配符都会被用作响应头的值。

注意

虽然这种通配符配置可能很方便,但建议在可能的情况下配置一组有限的值,以便提供更高级别的安全性。

@CrossOrigin

@CrossOrigin 注解可以启用被标注的控制器方法上的跨源请求,如下例所示:

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

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

@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}

默认情况下,@CrossOrigin 允许:

  • 所有来源。
  • 所有头部信息。
  • 控制器方法映射到的所有HTTP方法。

allowCredentials 默认是未启用的,因为启用该功能会建立一种信任关系,从而暴露用户的敏感信息(如 cookies 和 CSRF tokens),因此应仅在适当的情况下使用。当启用该功能时,要么需要将 allowOrigins 设置为一个或多个具体的域名(但不能是特殊值 „*“),要么可以使用 allowOriginPatterns 属性来匹配一组动态的来源域名。

maxAge 被设置为 30 分钟。

@CrossOrigin 也在类级别得到支持,并被所有方法继承。以下示例指定了一个特定的域名,并将 maxAge 设置为一小时:

@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}

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

@CrossOrigin(maxAge = 3600) 1
@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin("https://domain2.com") 2
@GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public MonoVoid> remove(@PathVariable Long id) {
// ...
}
}
  • 在类级别使用 @CrossOrigin

  • 在方法级别使用 @CrossOrigin

全局配置

除了细粒度的控制器方法级配置外,你可能还想定义一些全局的CORS配置。你可以单独在任何HandlerMapping上设置基于URL的CorsConfiguration映射。然而,大多数应用程序使用WebFlux Java配置来实现这一目标。

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

  • 所有来源。
  • 所有请求头。
  • GETHEADPOST 方法。

allowedCredentials 默认情况下是未启用的,因为启用该功能会暴露敏感的用户特定信息(如 cookie 和 CSRF 令牌),因此应该仅在适当的情况下使用。当启用该功能时,要么需要将 allowOrigins 设置为一个或多个具体的域名(但不能设置为特殊值 „*“),要么可以使用 allowOriginPatterns 属性来匹配一组动态的来源地址。

maxAge 被设置为 30 分钟。

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

@Configuration
public class WebConfig implements WebFluxConfigurer {

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

CORS WebFilter

你可以通过内置的 CorsWebFilter 来支持 CORS,该过滤器与 函数式端点 非常契合。

备注

如果你尝试将CorsFilter与Spring Security一起使用,请记住Spring Security已经内置了对CORS的支持(参见文档链接)。

要配置过滤器,您可以声明一个 CorsWebFilter bean,并向其构造函数传递一个 CorsConfigurationSource,如下例所示:

@Bean
CorsWebFilter corsFilter() {

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

return new CorsWebFilter(source);
}