此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

CORS

Spring WebFlux 允许您处理 CORS(跨域资源共享)。本节 描述了如何执行此作。spring-doc.cadn.net.cn

介绍

出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。 例如,您的银行账户可能位于一个选项卡中,而 evil.com 位于另一个选项卡中。脚本 从 evil.com 应该无法使用 凭据——例如,从您的账户中取款!spring-doc.cadn.net.cn

跨域资源共享 (CORS) 是大多数浏览器实现的 W3C 规范,允许您指定 什么样的跨域请求被授权,而不是使用不太安全、更少 基于 IFRAME 或 JSONP 的强大解决方法。spring-doc.cadn.net.cn

加工

CORS 规范区分了预检请求、简单请求和实际请求。 要了解 CORS 的工作原理,您可以阅读这篇文章,其中 许多其他,或有关更多详细信息,请参阅规范。spring-doc.cadn.net.cn

Spring WebFluxHandlerMapping实现为 CORS 提供内置支持。成功后 将请求映射到处理程序,则HandlerMapping检查 CORS 配置中的 给定的请求和处理程序,并采取进一步的作。处理印前检查请求 直接,而简单和实际的 CORS 请求被拦截、验证,并具有 设置了必需的 CORS 响应标头。spring-doc.cadn.net.cn

为了启用跨域请求(即Originheader 存在,并且 与请求的主机不同),您需要有一些显式声明的 CORS 配置。如果未找到匹配的 CORS 配置,则预检请求为 拒绝。不会将 CORS 标头添加到简单和实际 CORS 请求的响应中 因此,浏览器拒绝它们。spring-doc.cadn.net.cn

HandlerMapping可以使用基于 URL 模式的单独配置CorsConfiguration映射。在大多数情况下,应用程序 使用 WebFlux Java 配置声明此类映射,这会导致单个 全局地图传递给所有HandlerMapping实现。spring-doc.cadn.net.cn

您可以在HandlerMapping与更多水平 细粒度的处理程序级 CORS 配置。例如,带注释的控制器可以使用 类级或方法级@CrossOrigin注释(其他处理程序可以实现CorsConfigurationSource).spring-doc.cadn.net.cn

组合全局和本地配置的规则通常是累加的,例如, 所有全局和所有本地原点。对于那些只能使用单个值的属性 accepted,例如allowCredentialsmaxAge,则局部将覆盖全局值。看CorsConfiguration#combine(CorsConfiguration)了解更多详情。spring-doc.cadn.net.cn

若要从源代码中了解详细信息或进行高级自定义,请参阅:spring-doc.cadn.net.cn

凭证请求

将 CORS 与凭证请求一起使用需要启用allowedCredentials.请注意 此选项与配置的域建立了高度信任,并且还增加了 通过暴露敏感的用户特定信息对 Web 应用程序进行攻击的表面 例如 cookie 和 CSRF Tokens。spring-doc.cadn.net.cn

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

  • 通配符未授权allowOrigins,但也可以 这allowOriginPatterns属性可用于匹配一组动态源。spring-doc.cadn.net.cn

  • 当设置为allowedHeadersallowedMethodsAccess-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 配置。您可以设置基于 URL 的CorsConfiguration在任何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...
	}
}

CORSWebFilter

您可以通过内置的CorsWebFilter,这是一个 与功能端点配合良好。spring-doc.cadn.net.cn

如果您尝试使用CorsFilter使用 Spring Security,请记住 Spring 安全性内置了对 科斯。

要配置过滤器,您可以声明CorsWebFilterbean 并传递一个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)
}