From: https://docs.spring.io/spring-framework/reference/web/webmvc-cors.html
Spring MVC 允许处理 CORS(跨域资源共享)。本节介绍如何执行此操作。
一、介绍
出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。例如,您可以将银行帐户放在一个选项卡中,将 evil.com 放在另一个选项卡中。来自 sexy.com 的脚本不应该能够使用您的凭据向您的银行 API 发出 AJAX 请求 ——例如从您的帐户中提取资金!
跨源资源共享 (CORS) 是大多数浏览器实现的 W3C 规范,可让您指定授权哪种跨域请求,而不是使用基于 IFRAME 或 JSONP 的不太安全且功能较弱的解决方法。
二、处理
CORS 规范区分预检请求preflight、简单simple请求和实际actual请求。要了解 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
)。
组合全局和本地配置的规则通常是附加的 - 例如,所有全局和所有本地origins。对于那些只能接受单个值的属性,例如 allowCredentials
和 maxAge
,local覆盖global。有关详细信息,请参阅 CorsConfiguration#combine(CorsConfiguration)
。
Tip
要从源代码中了解更多信息或进行高级自定义,请检查后面的代码:
CorsConfiguration
CorsProcessor
,DefaultCorsProcessor
CorsProcessor
,DefaultCorsProcessor
AbstractHandlerMapping
三、@CrossOrigin
@CrossOrigin
注解支持对带注解的控制器方法进行跨源请求,如以下示例所示:
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
默认情况下, @CrossOrigin
允许:
- All origins
- All headers
- controller方法映射到的所有 HTTP 方法
allowCredentials
默认情况下不启用,因为它建立了一个暴露敏感的用户特定信息(例如 cookie 和 CSRF 令牌)的信任级别,并且只应在适当的情况下使用。启用后,必须将 allowOrigins
设置为一个或多个特定域(但不是特殊值 "*"
),或者可以使用 allowOriginPatterns
属性匹配一组动态的起源。
maxAge
设置为 30 分钟。
@CrossOrigin
在类级别也受支持,并且由所有方法继承,如以下示例所示:
@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
,如以下示例所示:
@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) {
// ...
}
}
四、Global Configuration 全局配置
除了细粒度的controller方法级别配置之外,您可能还想定义一些全局 CORS 配置。您可以在任何 HandlerMapping
上单独设置基于 URL 的 CorsConfiguration
映射。然而,大多数应用程序使用 MVC Java 配置或 MVC XML 命名空间来执行此操作。
默认情况下,全局配置启用以下功能:
- All origins
- All headers
GET
、HEAD
和POST
方法
allowCredentials
默认情况下不启用,因为它建立了一个暴露敏感的用户特定信息(例如 cookie 和 CSRF 令牌)的信任级别,并且只应在适当的情况下使用。启用后,必须将 allowOrigins
设置为一个或多个特定域(但不是特殊值 "*"
),或者可以使用 allowOriginPatterns
属性匹配一组动态的起源。
maxAge
设置为 30 分钟。
五、Java配置
要在 MVC Java 配置中启用 CORS,您可以使用 CorsRegistry 回调,如以下示例所示:
@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...
}
}
六、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
传递给其构造函数,如以下示例所示:
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);