说明
- 出于安全考虑,浏览器禁止AJAX调用驻留在当前来源之外的资源。例如,当您在一个标签中检查您的银行帐户时,您可以将evil.com网站放在另一个标签中。evil.com的脚本不能使用您的凭据向您的银行API发出AJAX请求(从您的帐户中提款)!
- 跨原始资源共享(CORS)是大多数浏览器实现的W3C规范,允许您以灵活的方式指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的黑客,如IFrame或JSONP。
- Spring Framework 4.2 GA为开箱即用的CORS提供了一流的支持,为您提供了比典型的基于过滤器的解决方案更简单和更强大的配置方式。
Spring MVC提供了高级配置功能
控制器方法CORS配置
-
您可以向
@RequestMapping
注释处理程序方法添加@CrossOrigin
注释,以便启用CORS(默认情况下@CrossOrigin
允许@RequestMapping
注释中指定的所有起始和HTTP
方法):@RestController @RequestMapping("/account") public class AccountController { @CrossOrigin @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { // ... }
}
-
也可以为整个控制器启用CORS:
@CrossOrigin(origins = "http://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) { // ... } }
在此示例中,对于这两种方法retrieve()
和remove()
处理程序方法都启用了CORS支持,还可以看到如何使用@CrossOrigin
属性自定义CORS配置。
您甚至可以同时使用控制器和方法级CORS配置,然后Spring将组合两个注释属性来创建合并的CORS配置。
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin(origins = "http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
如果您使用Spring Security,请确保在Spring Security级别启用CORS,并允许它利用Spring MVC级别定义的配置。
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
全局CORS配置
除了细粒度的基于注释的配置,您也可能想要定义一些全局CORS配置。这与使用过滤器类似,但可以使用Spring MVC声明并结合细粒度@CrossOrigin
配置。默认情况下,所有的起源和GET
,HEAD
和POST
方法都是允许的。
JavaConfig
为整个应用程序启用CORS类似于
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
如果您使用的是Spring Boot,建议只要声明一个WebMvcConfigurer
bean如下:
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
您可以轻松地更改任何属性,以及仅将此CORS配置应用于特定路径模式:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
如果您使用Spring Security,请确保在Spring Security级别启用CORS,并允许它利用Spring MVC级别定义的配置。
XML命名空间
也可以使用mvc XML命名空间配置CORS 。
这种最小的XML配置使得/**
路径模式上的CORS 具有与JavaConfig相同的默认属性:
也可以使用自定义属性声明几个CORS映射:
如果您使用Spring Security
,请不要忘记在Spring Security
级别启用CORS
:
...
它是如何工作的?
CORS请求(包括带有OPTIONS
方法的预检)请求被自动发送到已HandlerMapping
注册的各种。他们处理CORS预检要求和拦截CORS简单而实际的请求得益于CorsProcessor实现(DefaultCorsProcessor以添加相关CORS响应头(如默认情况下)Access-Control-Allow-Origin
)。CorsConfiguration允许您指定如何处理CORS请求:允许的起点,头,方法等。它可以以各种方式提供:
-
AbstractHandlerMapping#setCorsConfiguration()
允许在路径模式上映射Map
几个CorsConfiguration来指定一个/api/**
- 子类可以
CorsConfiguration
通过重写AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest)
方法提供自己的子类 - 处理程序可以实现
CorsConfigurationSource
接口(像ResourceHttpRequestHandler
现在这样),以便为每个请求提供CorsConfiguration。
基于过滤器的CORS支持
作为上述其他方法的替代方法,Spring Framework
还提供了一个CorsFilter。在这种情况下,而不是使用@CrossOrigin
或者WebMvcConfigurer#addCorsMappings(CorsRegistry)
您可以例如在Spring Boot应用程序中声明过滤器如下:
@Configuration
public class MyConfiguration {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}