一、CORS介绍
CORS是一种访问机制,英文全称: Cross-Origin Resource Sharing,即我们说的跨域资源共享。当一个资源从与该资源本身所在服务器不同的域或端口请求一个资源时,资源会发起一个跨域HTTP请求。比如,在一个域名下的网页中,调用另一个域名中的资源。
二、CORS的工作原理
CORS 实现跨域访问并不是一蹴而就的,需要借助浏览器的支持,从原理题图我们可以看到,简单的请求(通常指 GET/POST/HEAD 方式,并没有去增加额外的请求头信息) 直接创建了跨域请求的 XMLHttpRequest对象,而非简单请求(那种对服务器有特殊要求的请求,比如请求方法是 PUT 或DELETE,或者 Content-Type字段的类型是 application/json) 则要求先发送一个 "预检" 请求,待服务器批准后才能真正发起跨域访问请求。
三、Request Headers(请求头)
- Origin
表示跨域请求的原始域。 - Access-Control-Request-Method
表示跨域请求的方式。(如GET/POST) - Access-Control-Request-Headers
表示跨域请求的请求头信息。
四、Response headers(响应头 )
- Access-Control-Allow-Origin
表示允许哪些原始域进行跨域访问。(字符数组) - Access-Control-Allow-Credentials
表示是否允许客户端获取用户凭据。(布尔类型)
使用场景:例如现在从浏览器发起跨域请求,并且要附带Cookie信息给服务器。则必须具备两个条件:1. 浏览器端:发送AJAX请求前需设置通信对象XHR的withCredentials 属性为true。 2.服务器端:设置Access-Control-Allow-Credentials为true。两个条件缺一不可,否则即使服务器同意发送Cookie,浏览器也无法获取。 - Access-Control-Allow-Methods
表示跨域请求的方式的允许范围。(例如只授权GET/POST) - Access-Control-Allow-Headers
表示跨域请求的头部的允许范围。 - Access-Control-Expose-Headers
表示暴露哪些头部信息,并提供给客户端。(因为基于安全考虑,如果没有设置额外的暴露,跨域的通信对象XMLHttpRequest只能获取标准的头部信息) - Access-Control-Max-Age
表示预检请求的最大缓存时间。
五、实现CORS跨域请求
对于 CORS的跨域请求,主要有以下几种方式可供选择:
- 返回新的CorsFilter
- 重写 WebMvcConfigurer
- 使用注解 @CrossOrigin
- 手动设置响应头 (HttpServletResponse)
注意:
- CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,对应于springBoot 1.3版本以上
- 上面前两种方式属于全局 CORS 配置,后两种属性局部 CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过 @CrossOrigin 注解来进行细粒度更高的跨域资源控制。
1 CorsFilter(全局跨域)
在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}
2 重写 WebMvcConfigurer(全局跨域)
@Configuration
public class CorsConfig extends WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
3 继承WebMvcConfigurerAdapter(全局跨域)
@Configuration
class WebMvcConfigurer extends WebMvcConfigurerAdapter {
//跨域配置
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//放行哪些原始域
.allowedOrigins("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET", "POST", "PUT", "DELETE")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
}
};
}
}
4 使用注解 (局部跨域)
在控制器上使用注解 @CrossOrigin:
@RestController
@CrossOrigin(origins = "*")
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello world";
}
}
在方法上使用注解 @CrossOrigin:
@RequestMapping("/hello")
@CrossOrigin(origins = "*")
public String hello() {
return "hello world";
}
5 手动设置响应头(局部跨域)
使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 "*",表示全部放行。
@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Allow-Control-Origin","*");
return "index";
}