CORS
什么是 CORS
- 常见前端错误信息如下
Failed to load https://example.com/: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://XXX' is therefore not allowed access. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
上面的错误是因为浏览器的CORS机制导致的
Cross-Origin Resource Sharing (CORS)
COSR(跨站点资源分享)通俗的讲是跨域问题,严格来说它是跨域问题的解决方案之一,而且是官方解决方案。
在CORS成为标准之前,是没有办法请求不同域名的后端API的,因为安全原因。请求会被同源策略阻止,现在也是。
Access-Control-Allow-Origin
- 允许任意域名
*
- 完整的域名
https://example.com
如果你需要客户端传递验证信息到头部(比如:cookies)。这个值不能为 *
。
Access-Control-Allow-Credentials
这个头部信息只会在服务器支持通过cookies传递验证信息的返回数据里。
它的值只有一个就是 true
。
跨站点带验证信息时,服务器必须要争取设置这个值,服务器才能获取到用户的cookie。
Access-Control-Allow-Headers
提供一个逗号分隔的列表表示服务器支持的请求数据类型。
假如你使用自定义头部(比如:x-authentication-token
服务器需要在返回OPTIONS请求时,要把这个值放到这个头部里,否则请求会被阻止)。
Access-Control-Expose-Headers
相似的,这个返回信息里包含了一组头部信息,这些信息表示那些客户端可以使用。
其他没有在里面的头部信息将会被限制(译者注:这个头信息实战中使用较少)。
Access-Control-Allow-Methods
一个逗号分隔的列表,表明服务器支持的请求类型(比如:GET, POST)
Origin
这个头部信息,属于请求数据的一部分。这个值表明这个请求是从浏览器打开的哪个域名下发出的。出于安全原因,浏览器不允许你修改这个值
后端解决方式
SpringBoot
@Configuration
@EnableAsync
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.allowedOrigins("*")
// 允许凭证,解决session跨域丢失问题
.allowCredentials(true)
.maxAge(3600)
.exposedHeaders(
"Access-Control-Allow-Origin",
"Access-Control-Allow-Methods",
"Access-Control-Allow-Headers",
"Access-Control-Max-Age",
"Access-Control-Request-Headers",
"Access-Control-Request-Method"
);
}
}
CORS 的两次请求
跨域请求的时候,会出现两次请求。
一次 OPTION, 另一个才是真正的 GET/POST。
原因
第一个OPTIONS的请求是由Web服务器处理跨域访问引发的。
网上资料显示,OPTIONS是一种“预检请求”,浏览器在处理跨域访问的请求时如果判断请求为复杂请求,则会先向服务器发送一条预检请求, 根据服务器返回的内容浏览器判断服务器是否允许该请求访问。
如果 web 服务器采用 cors 的方式支持跨域访问,在处理复杂请求时这个预检请求是不可避免的。
查询代码发现,我们的web服务器确实采用的是cors来解决跨域访问的问题,并且我们在header中添加了自定义参数,导致我们的每次请求都为复杂请求,从而产生了每次请求都会发送两条请求的现象。
简而言之
OPTIONS请求方法的主要用途有两个:
1、获取服务器支持的HTTP请求方法;也是黑客经常使用的方法。
2、用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。
优化
问题的原因找到了,就要想办法解决这个问题。既然浏览器在处理复杂请求时,不可避免的要发送预检请求,那么能否减少预检请求的次数呢?
比如,预检一次设置一个有效期,在有效期内不再重复预检。
顺着这个思路,继续搜索相关资料,最终发现设置 Access-Control-Max-Age
这个参数即可达到预期目标。
该参数用来指定本次预检请求的有效期,单位为秒。在服务器上设置该参数之后,问题解决了。
参考资料
https://segmentfault.com/q/1010000008992437
https://blog.csdn.net/cnhnnyzhy/article/details/53128179
https://zhuanlan.zhihu.com/p/29980092
https://zhuanlan.zhihu.com/p/31935253