CORS
Spring MVC 允许您处理 CORS(跨域资源共享)。本节介绍如何实现。
凭证请求
将 CORS 与凭证请求一起使用需要启用 allowedCredentials
。请注意,此选项与配置的域建立了高度信任,并通过暴露敏感的用户特定信息(如 cookie 和 CSRF 令牌)增加了 Web 应用程序的攻击面。
启用凭证还会影响配置的 "*"
CORS 通配符的处理方式:
-
allowOrigins
中不允许使用通配符,但可以使用allowOriginPatterns
属性来匹配一组动态来源。 -
当在
allowedHeaders
或allowedMethods
上设置时,Access-Control-Allow-Headers
和Access-Control-Allow-Methods
响应头通过复制 CORS 预检请求中指定的相关头和方法来处理。 -
当在
exposedHeaders
上设置时,Access-Control-Expose-Headers
响应头要么设置为配置的头列表,要么设置为通配符字符。虽然 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
@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
默认不启用,因为它建立了一个信任级别,会暴露敏感的用户特定信息(例如 cookie 和 CSRF 令牌),因此应仅在适当的情况下使用。当启用时,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
默认不启用,因为它建立了一个信任级别,会暴露敏感的用户特定信息(例如 cookie 和 CSRF 令牌),因此应仅在适当的情况下使用。当启用时,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 支持。
如果您尝试将 |
要配置过滤器,请将 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)