跨域资源共享CORS详解

366 阅读4分钟

在不久前的工作中,遇到了跨域的问题,当时对与浏览器在跨域的整个的过程不是很清楚,所以就写下这篇文章,分享一下当发送跨域请求的时候,浏览器做了一些什么操作。

起因

在之前对接调试的时候,我们需要调用第三方的接口去拿到数据,这样的话就不符合浏览器的同源策略即 协议、端口号、域名相同。本次主要是使用了cors来解决跨域问题的,cors解决跨域也比较常见,虽然在我们的日常开发中遇到跨域的情况不多,但是我认为还是需要对于这种行为有一定的了解。

跨域资源共享(CORS)

跨域资源共享(cors)是一个W3C标准,它允许浏览器向跨源服务器,发出XMLHttpRequest请求。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与普通的AJAX通信没有差别,代码完全一样。浏览器一旦发现跨域请求,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨域通信。

为了方便理解,我这边使用了express简单的搭建了一个服务器,用来接受浏览器发送的cors请求,以及做一下相应的处理,通过端口号不同来做跨域请求

浏览器对cors请求分为两种请求,分别为简单请求和非简单请求。

简单请求

简单请求是指 同时 满足一下条件的:

1、请求类型是 HEAD、GET、POST这三种类型的其中之一

2、HTTP请求头字段不超过一下几种:
    Accpt
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type: 只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
  • 简单请求的基本流程

对于简单请求,浏览器会直接发送cors请求,在请求的头部添加Origin字段,

Orgin字段表示本次请求的协议、域名和端口号,服务器会用这个信息去决定是否同意这次请求,如果Origin指定的源包含在服务器指定源的范围之内,服务器返回的响应就会多出几个头信息

Access-Control-Allow-Origin: http://xxx.xxx.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: dt-X
Content-Type: text/html; charset=utf-8

介绍一下以上三个与cors有关的字段

  • Access-Control-Allow-Origin: 这个值是必须的,该字段必须为Origin字段的值,或者为*,表示可以接受任意源的请求
  • Access-Control-Allow-Credentials: 该字段可选,表示为是否允许发送cookie
  • Access-Control-Expose-Headers: 该字段可选,如果想在请求头中获取其他字段的话,需要设置该值

非简单请求

非简单请求是指是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。一般来说,我们在开发的时候使用的都是非简单请求。

非简单请求会在正式通信之前,发起一次HTTP请求,称为预检请求。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP的头信息字段。只有得到肯定答复,浏览器才会发出正式的POST请求,否则就报错。

服务器收到预检请求之后,检查了请求的Origin、Access-Control-Request-Method、Access-Control-Request-Headers之后,确认可以跨源,就会返回响应;如果将服务器端Access-Control-Allow-Origin的值设置为http://127.0.0.1:3011,不通过预检请求,浏览器则会提示如下错误

Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' 

header has a value 'http://127.0.0.1:3011' that is not equal to the supplied origin.

为了避免预检请求option重复请求,服务器可以在返回的响应中添加Access-Control-Max-Age字段,那么在这个时间段之内,浏览器就不再发送第二预检请求了。

结果

在之前的调试工作的时候,由于对于浏览器的预检请求认识不够,并且也没有在现在去调试出现的问题,在使用第三方接口的时候,一直没有拿到相应的数据,即使后面又发送了post请求,当时是认为因为预检请求没有返回内容,所以在当时没有拿到正确的数据,现在可以看一下,其实预检请求对于我们发送的请求是没有影响,我们还是可以正确拿到返回的内容的。最后这个问题我们使用的处理方法是,让后端去帮我去调用第三方的接口,我来调用后端提供的接口,从而避开跨域的请求。

相关链接

developer.mozilla.org/zh-CN/docs/…

www.ruanyifeng.com/blog/2016/0…