CORS

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 这个参数即可达到预期目标。

该参数用来指定本次预检请求的有效期,单位为秒。在服务器上设置该参数之后,问题解决了。

参考资料

跨域资源共享 CORS 详解

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