|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
CORS
Spring MVC 允许你处理 CORS(跨域资源共享)。本节描述了如何进行处理。
简介
出于安全原因,浏览器禁止向当前源以外的资源发起 AJAX 调用。例如,你可以在一个标签页中打开你的银行账户,在另一个标签页中打开 evil.com。evil.com 中的脚本不应该能够使用你的凭证向你的银行 API 发起 AJAX 请求——例如从你的账户中取款!
跨源资源共享(CORS)是由W3C规范实现的,大多数浏览器都支持它,它允许你指定授权的跨域请求类型,而不是使用基于IFRAME或JSONP的不安全且功能较弱的变通方案。
带凭证的请求
使用带有凭据的 CORS 请求需要启用 allowedCredentials。请注意,此选项会与配置的域名建立较高的信任级别,并且通过暴露如 cookies 和 CSRF Tokens等敏感用户信息,增加了网络应用程序的攻击面。
启用凭据也会影响配置的 "*" CORS 通配符的处理方式:
-
通配符在<code>0</code>中不被授权,但可以使用<code>1</code>属性来匹配一组动态的来源。
-
当设置为
allowedHeaders或allowedMethods时,Access-Control-Allow-Headers和Access-Control-Allow-Methods响应头由复制相关 在 CORS 预检请求中指定的头信息和方法来处理。 -
当设置为
exposedHeaders时,响应头被设置为配置的头列表或通配符。虽然 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)。
合并全局和本地配置的规则通常是累加的 — 例如,所有全局和本地源。对于只接受单个值的属性,例如 allowCredentials 和 maxAge,本地值会覆盖全局值。有关更多详细信息,请参见
CorsConfiguration#combine(CorsConfiguration)
。
|
要从源代码中学习更多或进行高级自定义,请查看背后的代码:
|
@CrossOrigin
The @CrossOrigin
注解允许在注解的控制器方法上启用跨域请求,如下例所示:
-
Java
-
Kotlin
@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 允许:
-
所有源。
-
所有头部。
-
控制器方法映射的所有HTTP方法。
allowCredentials 默认未启用,因为这会建立一个信任级别,会暴露敏感的用户特定信息(如 cookies 和 CSRF Tokens),并且只能在适当的情况下使用。当启用时,必须将 allowOrigins 设置为一个或多个特定域名(但不能是特殊值 "*"),或者可以使用 allowOriginPatterns 属性来匹配动态的一组来源。
maxAge 设置为 30 分钟。
@CrossOrigin 在类级别也是支持的,并且被所有方法继承,如下例所示:
-
Java
-
Kotlin
@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,如下例所示:
-
Java
-
Kotlin
@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命名空间来实现这一点。
默认情况下,全局配置启用了以下功能:
-
所有源。
-
所有头部。
-
GET,HEAD, 和POST方法。
allowCredentials 默认未启用,因为这会建立一个信任级别,会暴露敏感的用户特定信息(如 cookies 和 CSRF Tokens),并且只能在适当的情况下使用。当启用时,必须将 allowOrigins 设置为一个或多个特定域名(但不能是特殊值 "*"),或者可以使用 allowOriginPatterns 属性来匹配动态的一组来源。
maxAge 设置为 30 分钟。
Java配置
要启用MVC Java配置中的CORS,可以使用CorsRegistry回调,
如下例所示:
-
Java
-
Kotlin
@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>元素,
如下例所示:
<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支持。
如果你尝试使用CorsFilter与Spring Security一起,需要注意的是Spring Security具有
内置支持
用于CORS。 |
要配置该过滤器,请在构造函数中传递一个CorsConfigurationSource,如下例所示:
-
Java
-
Kotlin
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)