对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

跨域资源共享(CORS)

Spring WebFlux 允许你处理 CORS(跨域资源共享)。本节将介绍如何实现这一点。spring-doc.cadn.net.cn

< Introduction >

出于安全原因,浏览器禁止向当前源(origin)之外的资源发起 AJAX 请求。 例如,你可能在一个标签页中打开了你的银行账户页面,而在另一个标签页中打开了 evil.com。来自 evil.com 的脚本不应能够使用你的凭据向你的银行 API 发起 AJAX 请求——例如,从你的账户中提取资金!spring-doc.cadn.net.cn

跨域资源共享(CORS)是一种W3C 规范, 已被大多数浏览器实现,它允许你指定 哪些类型的跨域请求是被授权的,而无需使用基于 IFRAME 或 JSONP 的安全性较低且功能较弱的变通方法。spring-doc.cadn.net.cn

处理

CORS 规范区分了预检请求(preflight)、简单请求(simple)和实际请求(actual)。 要了解 CORS 的工作原理,您可以阅读 这篇文章, 以及其他许多资料,或查阅规范以获取更多详细信息。spring-doc.cadn.net.cn

Spring WebFlux 的 HandlerMapping 实现提供了对 CORS 的内置支持。在成功将请求映射到处理器之后,HandlerMapping 会检查该请求和处理器对应的 CORS 配置,并执行后续操作。预检(Preflight)请求会被直接处理,而简单请求和实际的 CORS 请求则会被拦截、验证,并设置所需的 CORS 响应头。spring-doc.cadn.net.cn

为了启用跨域请求(即请求中包含 Origin 头部,且其值与请求的主机不同),您需要显式声明一些 CORS 配置。如果未找到匹配的 CORS 配置,预检(preflight)请求将被拒绝。简单 CORS 请求和实际 CORS 请求的响应中不会添加任何 CORS 相关头部,因此浏览器会拒绝这些请求。spring-doc.cadn.net.cn

每个 HandlerMapping 都可以 单独配置 基于 URL 模式的 CorsConfiguration 映射。在大多数情况下,应用程序使用 WebFlux 的 Java 配置来声明此类映射,从而生成一个全局的映射,并将其传递给所有的 HandlerMapping 实现。spring-doc.cadn.net.cn

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

全局配置与本地配置的组合规则通常是累加的——例如,所有全局来源和所有本地来源。对于仅能接受单个值的属性(如 allowCredentialsmaxAge),本地配置将覆盖全局配置的值。详见 CorsConfiguration#combine(CorsConfiguration) 以获取更多详细信息。spring-doc.cadn.net.cn

要从源码中了解更多内容或进行高级自定义,请参阅:spring-doc.cadn.net.cn

凭据请求

在使用 CORS 进行带凭证的请求时,需要启用 allowedCredentials。请注意,此选项会与所配置的域名建立高度信任关系,同时也会通过暴露敏感的用户特定信息(例如 Cookie 和 CSRF Tokens)而扩大 Web 应用程序的攻击面。spring-doc.cadn.net.cn

启用凭据还会影响已配置的 "*" CORS 通配符的处理方式:spring-doc.cadn.net.cn

  • allowOrigins 中不允许使用通配符,但可以改用 allowOriginPatterns 属性来匹配一组动态的源。spring-doc.cadn.net.cn

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

  • 当设置在 exposedHeaders 上时,响应头 Access-Control-Expose-Headers 会被设置为配置的头部列表或通配符。尽管 CORS 规范在 Access-Control-Allow-Credentials 设置为 true 时不允許使用通配符,但大多数浏览器都支持该用法,并且在 CORS 处理过程中并非所有响应头都可用,因此无论 allowCredentials 属性的值如何,只要指定了通配符,就会将其作为头部的值使用。spring-doc.cadn.net.cn

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

@CrossOrigin

@CrossOrigin 注解可在带注解的控制器方法上启用跨域请求,如下例所示:spring-doc.cadn.net.cn

@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) {
		// ...
	}
}
@RestController
@RequestMapping("/account")
class AccountController {

	@CrossOrigin
	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}

默认情况下,@CrossOrigin 允许:spring-doc.cadn.net.cn

allowCredentials 默认未启用,因为启用它会建立一种信任级别,暴露敏感的用户特定信息(例如 Cookie 和 CSRF Tokens),应仅在适当的情况下使用。启用该选项时,必须将 allowOrigins 设置为一个或多个特定域名(但不能使用特殊值 "*"),或者也可以使用 allowOriginPatterns 属性来匹配一组动态的源。spring-doc.cadn.net.cn

maxAge 被设置为 30 分钟。spring-doc.cadn.net.cn

@CrossOrigin 也支持在类级别上使用,并且会被所有方法继承。 以下示例指定了特定的域名,并将 maxAge 设置为一小时:spring-doc.cadn.net.cn

@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("https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {

	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}

你可以在类级别和方法级别同时使用 @CrossOrigin,如下例所示:spring-doc.cadn.net.cn

@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 Mono<Void> remove(@PathVariable Long id) {
		// ...
	}
}
1 在类级别使用 @CrossOrigin
2 在方法级别使用 @CrossOrigin
@CrossOrigin(maxAge = 3600) (1)
@RestController
@RequestMapping("/account")
class AccountController {

	@CrossOrigin("https://domain2.com") (2)
	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}
1 在类级别使用 @CrossOrigin
2 在方法级别使用 @CrossOrigin

全局配置

除了细粒度的控制器方法级别配置外,您可能还想定义一些全局的 CORS 配置。您可以在任意 CorsConfiguration 上单独设置基于 URL 的 HandlerMapping 映射。然而,大多数应用程序使用 WebFlux Java 配置来实现这一点。spring-doc.cadn.net.cn

默认情况下,全局配置启用了以下内容:spring-doc.cadn.net.cn

allowedCredentials 默认未启用,因为启用它会建立一种信任级别,暴露敏感的用户特定信息(例如 Cookie 和 CSRF Tokens),应仅在适当的情况下使用。启用该选项时,必须将 allowOrigins 设置为一个或多个特定域名(但不能使用特殊值 "*"),或者也可以使用 allowOriginPatterns 属性来匹配一组动态的源。spring-doc.cadn.net.cn

maxAge 被设置为 30 分钟。spring-doc.cadn.net.cn

要在 WebFlux 的 Java 配置中启用 CORS,您可以使用 CorsRegistry 回调,如下例所示:spring-doc.cadn.net.cn

@Configuration
@EnableWebFlux
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...
	}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {

	override fun addCorsMappings(registry: CorsRegistry) {

		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 支持, 它与 函数式端点 非常契合。spring-doc.cadn.net.cn

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

要配置该过滤器,您可以声明一个 CorsWebFilter Bean,并将其构造函数传入一个 CorsConfigurationSource,如下例所示:spring-doc.cadn.net.cn

@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);
}
@Bean
fun corsFilter(): CorsWebFilter {

	val config = CorsConfiguration()

	// Possibly...
	// config.applyPermitDefaultValues()

	config.allowCredentials = true
	config.addAllowedOrigin("https://domain1.com")
	config.addAllowedHeader("*")
	config.addAllowedMethod("*")

	val source = UrlBasedCorsConfigurationSource().apply {
		registerCorsConfiguration("/**", config)
	}
	return CorsWebFilter(source)
}