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

CORS

Spring MVC 允许你处理 CORS(跨域资源共享)。本节描述了如何进行处理。spring-doc.cadn.net.cn

简介

出于安全原因,浏览器禁止向当前源以外的资源发起 AJAX 调用。例如,你可以在一个标签页中打开你的银行账户,在另一个标签页中打开 evil.com。evil.com 中的脚本不应该能够使用你的凭证向你的银行 API 发起 AJAX 请求——例如从你的账户中取款!spring-doc.cadn.net.cn

跨源资源共享(CORS)是由W3C规范实现的,大多数浏览器都支持它,它允许你指定授权的跨域请求类型,而不是使用基于IFRAME或JSONP的不安全且功能较弱的变通方案。spring-doc.cadn.net.cn

带凭证的请求

使用带有凭据的 CORS 请求需要启用 allowedCredentials。请注意,此选项会与配置的域名建立较高的信任级别,并且通过暴露如 cookies 和 CSRF Tokens等敏感用户信息,增加了网络应用程序的攻击面。spring-doc.cadn.net.cn

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

  • 通配符在<code>0</code>中不被授权,但可以使用<code>1</code>属性来匹配一组动态的来源。spring-doc.cadn.net.cn

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

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

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

处理

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

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

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

每个 HandlerMapping 可以通过 配置 基于 URL 模式的 CorsConfiguration 映射单独进行配置。在大多数情况下,应用程序使用 MVC Java 配置或 XML 命名空间来声明这些映射,这会导致所有 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

@CrossOrigin

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

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

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

	@DeleteMapping("/{id}")
	public void remove(@PathVariable Long id) {
		// ...
	}
}
@RestController
@RequestMapping("/account")
class AccountController {

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

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

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

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

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

@CrossOrigin 在类级别也是支持的,并且被所有方法继承,如下例所示:spring-doc.cadn.net.cn

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

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

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

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

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

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

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

全局配置

除了细粒度的控制器方法级别的配置,你可能还想定义一些全局的CORS配置。你可以为任何HandlerMapping单独设置基于URL的CorsConfiguration映射。然而,大多数应用程序使用MVC Java配置或MVC XML命名空间来实现这一点。spring-doc.cadn.net.cn

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

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

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

Java配置

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

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

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

XML配置

要启用XML命名空间中的CORS,可以使用<mvc:cors>元素, 如下例所示:spring-doc.cadn.net.cn

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

CORS过滤器

您可以通过内置的 CorsFilter应用CORS支持。spring-doc.cadn.net.cn

如果你尝试使用CorsFilter与Spring Security一起,需要注意的是Spring Security具有 内置支持 用于CORS。

要配置该过滤器,请在构造函数中传递一个CorsConfigurationSource,如下例所示:spring-doc.cadn.net.cn

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);
val config = CorsConfiguration()

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

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

val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", config)

val filter = CorsFilter(source)