此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
CORS
Spring WebFlux 允许您处理 CORS(跨域资源共享)。本节 描述了如何执行此作。
介绍
出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。 例如,您的银行账户可能位于一个选项卡中,而 evil.com 位于另一个选项卡中。脚本 从 evil.com 应该无法使用 凭据——例如,从您的账户中取款!
加工
CORS 规范区分了预检请求、简单请求和实际请求。 要了解 CORS 的工作原理,您可以阅读这篇文章,其中 许多其他,或有关更多详细信息,请参阅规范。
Spring WebFluxHandlerMapping
实现为 CORS 提供内置支持。成功后
将请求映射到处理程序,则HandlerMapping
检查 CORS 配置中的
给定的请求和处理程序,并采取进一步的作。处理印前检查请求
直接,而简单和实际的 CORS 请求被拦截、验证,并具有
设置了必需的 CORS 响应标头。
为了启用跨域请求(即Origin
header 存在,并且
与请求的主机不同),您需要有一些显式声明的 CORS
配置。如果未找到匹配的 CORS 配置,则预检请求为
拒绝。不会将 CORS 标头添加到简单和实际 CORS 请求的响应中
因此,浏览器拒绝它们。
每HandlerMapping
可以使用基于 URL 模式的单独配置CorsConfiguration
映射。在大多数情况下,应用程序
使用 WebFlux Java 配置声明此类映射,这会导致单个
全局地图传递给所有HandlerMapping
实现。
您可以在HandlerMapping
与更多水平
细粒度的处理程序级 CORS 配置。例如,带注释的控制器可以使用
类级或方法级@CrossOrigin
注释(其他处理程序可以实现CorsConfigurationSource
).
组合全局和本地配置的规则通常是累加的,例如,
所有全局和所有本地原点。对于那些只能使用单个值的属性
accepted,例如allowCredentials
和maxAge
,则局部将覆盖全局值。看CorsConfiguration#combine(CorsConfiguration)
了解更多详情。
若要从源代码中了解详细信息或进行高级自定义,请参阅:
|
凭证请求
将 CORS 与凭证请求一起使用需要启用allowedCredentials
.请注意
此选项与配置的域建立了高度信任,并且还增加了
通过暴露敏感的用户特定信息对 Web 应用程序进行攻击的表面
例如 cookie 和 CSRF Tokens。
启用凭据还会影响配置的 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
财产。
虽然这种通配符配置很方便,但建议在可能的情况下配置 有限的值集,以提供更高级别的安全性。 |
@CrossOrigin
这@CrossOrigin
注释启用带注释的控制器方法上的跨域请求,如
以下示例显示:
-
Java
-
Kotlin
@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
允许:
-
所有起源。
-
所有标头。
-
控制器方法映射到的所有 HTTP 方法。
allowCredentials
默认情况下不启用,因为这会建立信任级别
暴露敏感的用户特定信息(例如 cookie 和 CSRF Tokens),以及
应仅在适当的情况下使用。启用时allowOrigins
必须是
设置为一个或多个特定域(但不是特殊值)或
这"*"
allowOriginPatterns
属性可用于匹配一组动态源。
maxAge
设置为 30 分钟。
@CrossOrigin
在类级别也受支持,并被所有方法继承。
以下示例指定某个域,并将maxAge
到一个小时:
-
Java
-
Kotlin
@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
在类和方法级别,
如以下示例所示:
-
Java
-
Kotlin
@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 配置来执行此作。
默认情况下,全局配置启用以下功能:
-
所有起源。
-
所有标头。
-
GET
,HEAD
和POST
方法。
allowedCredentials
默认情况下不启用,因为这会建立信任级别
暴露敏感的用户特定信息(例如 cookie 和 CSRF Tokens),以及
应仅在适当的情况下使用。启用时allowOrigins
必须是
设置为一个或多个特定域(但不是特殊值)或
这"*"
allowOriginPatterns
属性可用于匹配一组动态源。
maxAge
设置为 30 分钟。
要在 WebFlux Java 配置中启用 CORS,您可以使用CorsRegistry
回调
如以下示例所示:
-
Java
-
Kotlin
@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
,这是一个
与功能端点配合良好。
如果您尝试使用CorsFilter 使用 Spring Security,请记住 Spring
安全性内置了对
科斯。 |
要配置过滤器,您可以声明CorsWebFilter
bean 并传递一个CorsConfigurationSource
到其构造函数,如以下示例所示:
-
Java
-
Kotlin
@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)
}