何为CORS-跨域资源共享?

1.何为CORS?

CORS全称为Cross-Origin Resource Sharing,跨源资源共享(俗称跨域资源共享)。

照搬HTTP文档对CORS的定义:CORS是一种机制,该机制使用附加的HTTP header来告诉浏览器,准许运行在一个源上的Web应用访问位于另一不同源选定的资源。 当一个Web应用发起一个与自身所在源(域,协议和端口)不同的HTTP请求时,它发起的即跨源HTTP请求。

如果觉得中文绕口,可以看英文:Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.

不理解没关系,接着往下看,后面会再做解读。

首先,举个两个例子来方便理解何为跨源:

1.用户直接访问百度,这不叫跨源。因为请求来自用户的浏览器,而不是某个服务器(源)。

2.用户访问A.com,按下页面上的某个按钮后,A网站上的ajax代码运行,希望访问B.com上的资源,从而对自己的页面进行动态加载。这时,A源希望访问B源,那么这种行为就是跨源。事实上,只要域名、协议、端口有任一不同,都算跨源。

出于安全性,浏览器限制例2这种脚本内发起的跨源HTTP请求。 ajax遵循同源策略(same-origin policy), 这意味着使用ajax的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

一般来说,最常见的跨域情景,是ajax跨域网站加载第三方字体的时候。工作中遇到的需要跨域的情况,大多数都是使用ajax的时候。

2.CORS的运作原理

如何进行跨域访问呢?在CORS出来以前,有使用flash插件发送请求,以绕开浏览器限制的;还有使用代理服务器转发的。而到了HTML5以后,新的跨域策略诞生了:CORS

使用CORS后的跨域访问过程,其实一句话就可以概括:A想访问B的资源,按照同源策略,为了保护B,这是不允许的。但如果B本身允许A访问呢?那B就告诉浏览器:我允许A跨域访问我!于是A就可以开开心心的读取B的响应内容了。

上述过程,是通过在请求和响应的http中——添加特定的http header——来完成的。接下来详细屡一下流程。

首先,用户正在使用A.com提供的web应用程序,此时用户的某个操作触发了A.com的ajax脚本,于是该脚本对B.com发送了请求。由于是跨源请求,A在http请求头中添加了一行Origin字段:

GET /resources/public-data/ HTTP/1.1
Host: B.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://A.com

当B服务器接收到该请求后,看到请求头中有Origin字段,明白了这是来自A源的CORS跨域请求。此时,B服务器会按正常行为处理该请求并正常返回response。但是,在响应头中,会添加这么一行字段:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

当浏览器接收到该响应后,它会查看响应头中的Access-Control-Allow-Origin字段是否包含本域(A.com),如果包含,则将该响应交给发出该ajax请求的js脚本。星号表示全匹配。

以上就是最简单的一次CORS跨域请求,这其中有两点需要注意:

1.想要跨域访问,决定权掌握在对方手中,看且只看对方同不同意(此例中的B服务器)。

2.是浏览器来判断该响应能否交给js,而不是对方服务器。换句话说就是,服务器是永远都会返回正常响应的,而这个响应最终能不能交到ajax调用方的手上(能否跨域成功),是浏览器在做决定。

此时再回头看看CORS的定义,就能明白,CORS机制,就是通过在http头部添加一些信息,让浏览器准许一个源获取另一个源的资源。浏览器才是大哥,是这次跨域通信的交通警察。

如果跨域失败,直接按F12的话是可以看到服务器的响应内容的。而如果让js打印出response,则毛都打印不出来。因为浏览器代表用户,而js代表源(比如A站的站长),用户看到B站的response是没事的,就像用户正常访问百度一样,这不是跨域。但js如果不经B站同意就看到了B站发来的response,那背后的程序员(A站站长)可能会干坏事,这是不允许的,浏览器说:“不行,我就算收到了B站的响应,也不交给你A站,因为B站不让(此指跨域失败的情况)。”

3.更复杂的CORS使用场景(带凭证)

HTTP文档提供了3类CORS的使用场景。上面的例子属于简单请求(Simple requests),还有预检请求(Preflighted requests)和带凭证请求(Requests with credentials)。具体的内容见文档(后面会给出链接),这里只针对带凭证请求,具体的讲一下怎么做(原理请看文档),毕竟需要操作cookie的情景还是很常见的。

  1. 一般来说,当ajax需要发送身份凭证时,浏览器是不会允许的。此时,前端需要设置withCredentials = true。
  2. 对于header携带凭证的请求,服务器的响应头中,Access-Control-Allow-Origin不得设置为*,必须显式指定源。
  3. 服务器的响应头中,Access-Control-Allow-Credentials指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容,需要设置成True。
  4. 跨源访问时,ajax脚本只能从响应中获取一些最基本的http头,其中不包括Set-Cookie。这不是因为服务器没发送Set-Cookie,而是被浏览器拦截了,又一次的,浏览器充当了交通警察的作用。此时需要服务器主动将该字段暴露出去。在响应头中添加
    Access-Control-Expose-Headers: 'Set-Cookie'

    这代表服务器允许其他源获取Set-Cookie头部字段,此时浏览器才会把该信息安心地交给脚本。

如果上述4条都无法解决问题,建议了解cookie的domain原则和“第三方cookie”。有时间的话会继续更。

参考资料:

1.https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

2.https://www.liaoxuefeng.com/wiki/1022910821149312/1023022332902400

3.https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies

posted @ 2020-09-25 23:00  ShiveryMoon  阅读(273)  评论(0编辑  收藏  举报