CORS 跨域共享
跨源资源共享(Cross-origin resource sharing, CORS)是由大多数浏览器实现的W3C规范,它允许您以灵活的方式指定哪种跨域请求被授权,而不是使用一些不太安全、功能不太强大的方法,比如IFRAME或JSONP。
Spring BOOT
从4.2开始,Spring MVC已支持CORS。
@CrossOrigin
在Spring Boot中使用带有@CrossOrigin注释的controller方法CORS配置,不需要任何特定的配置。
@CrossOrigin注解可以在类上使用,也可以在方法上使用,如:
@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) {
// ...
}
}
常用的属性有2个,分别是origins和maxAge,下面分别解释下:
- origins 是一个字符串数组的类型(String []),用于表示那些域名可以访问此接口/映射。多个域名之间用逗号“,”隔开,如果想向所有网址开放,可以直接填写星号“*”。
- maxAge 还没有找到官方的说明,其他开发者解释为“预请求的结果的有效期”,即COOKIE的有效期,单位是秒。
如果只在方法上或者类上使用@CrossOrigin注解,则默认该映射接受所有的网站发送过来的请求、接受所有类型的http请求和接受所有的不同内容的头部信息(Header),如:
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@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("http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
remove方法可以被所有网站的访问,retrieve方法只接受http://domain2.com网站发送的请求。
全局CORS配置
全局CORS配置可以通过注册一个WebMvcConfigurer bean并使用自定义的addcorsmapping (CorsRegistry)方法来定义,如:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}
拦截器
另外,还可以通过添加 Filter 的方式,配置 CORS 规则,并手动指定对哪些接口有效。
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:9000");
config.addAllowedOrigin("null");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
原理剖析
无论是通过哪种方式配置 CORS
,其实都是在构造 CorsConfiguration
。 一个 CORS
配置用一个 CorsConfiguration
类来表示,它的定义如下:
public class CorsConfiguration {
private List<String> allowedOrigins;
private List<String> allowedMethods;
private List<String> allowedHeaders;
private List<String> exposedHeaders;
private Boolean allowCredentials;
private Long maxAge;
}
Spring
中对 CORS
规则的校验,都是通过委托给 DefaultCorsProcessor
实现的。
DefaultCorsProcessor
处理过程如下:
- 判断依据是
Header
中是否包含Origin
。如果包含则说明为CORS
请求,转到 2;否则,说明不是CORS
请求,不作任何处理。 - 判断
response
的Header
是否已经包含Access-Control-Allow-Origin
,如果包含,证明已经被处理过了, 转到 3,否则不再处理。 - 判断是否同源,如果是则转交给负责该请求的类处理
- 是否配置了
CORS
规则,如果没有配置,且是预检请求,则拒绝该请求,如果没有配置,且不是预检请求,则交给负责该请求的类处理。如果配置了,则对该请求进行校验。
校验就是根据 CorsConfiguration
这个类的配置进行判断:
- 判断
origin
是否合法 - 判断
method
是否合法 - 判断
header
是否合法 - 如果全部合法,则在
response header
中添加响应的字段,并交给负责该请求的类处理,如果不合法,则拒绝该请求。