简介
什么是跨域资源共享呢? 咱们晓得一个域是由scheme、domain和port三局部来组成的,这三个局部能够惟一标记一个域,或者一个服务器申请的地址。跨域资源共享的意思就是服务器容许其余的域来拜访它本人域的资源。
CORS是一个基于HTTP-header检测的机制,本文将会具体对其进行阐明。
CORS举例
为了平安起见,个别一个域发动的申请只能获取该域本人的资源,因为域资源外部的相互调用被认为是平安的。
然而随着古代浏览器技术和ajax技术的倒退,慢慢的呈现了从javascript中去申请其余域资源的需要,咱们把这样的需要叫做跨域申请。
比如说客户端从域http://www.flydean.com向域http://www.abc.com/data.json申请数据。
那么客户端是怎么晓得服务器是否反对CORS的呢?
这里会应用到一个叫做preflight的申请,这个申请只是向服务器确认是否反对要拜访资源的跨域申请,当客户端失去响应之后,才会真正的去申请服务器中的跨域资源。
尽管是客户端去设置HTTP申请的header来进行CORS申请,然而服务端也须要进行一些设置来保障可能响应客户端的申请。所以本文同时适宜前端开发者和后端开发者。
CORS protocol
没错,任意一种申请要想标准化,那么必须制订规范的协定,CORS也一样,CORS protocol次要定义了HTTP中的申请头和响应头。咱们别离来具体理解。
HTTP request headers
首先是HTTP的申请头。申请头是客户端申请资源时所带的数据。CORS申请头次要蕴含三局部。
第一局部是Origin,示意发动跨域资源申请的request或者preflight request源:
Origin: <origin>
Origin只蕴含server name信息,并不蕴含任何PATH信息。
留神,Origin的值可能为null
第二局部是Access-Control-Request-Method,这是一个preflight request,通知服务器下一次真正会应用的HTTP资源申请办法:
Access-Control-Request-Method: <method>
第三局部是Access-Control-Request-Headers,同样也是一个preflight request,通知服务器下一次真正应用的HTTP申请中要带的header数据。header中的数据是和server端的Access-Control-Allow-Headers绝对应的。
Access-Control-Request-Headers: <field-name>[, <field-name>]*
HTTP response headers
有了客户端的申请,还须要服务器端的响应,咱们看下服务器端都须要设置那些HTTP header数据。
- Access-Control-Allow-Origin
Access-Control-Allow-Origin示意服务器容许的CORS的域,能够指定特定的域,也能够应用*示意接管所有的域。
Access-Control-Allow-Origin: <origin> | *
要留神的是,如果申请带有认证信息,则不能应用*。
咱们看一个例子:
Access-Control-Allow-Origin: http://www.flydean.comVary: Origin
下面例子示意服务器容许接管来自http://www.flydean.com的申请,这里指定了具体的某一个域,而不是应用*。因为服务器端能够设置一个容许的域列表,所以这里返回的只是其中的一个域地址,所以还须要在上面加上一个Vary:Origin头信息,示意Access-Control-Allow-Origin会随客户端申请头中的Origin信息主动发送变动。
- Access-Control-Expose-Headers
Access-Control-Expose-Headers示意服务器端容许客户端或者CORS资源的同时可能拜访到的header信息。其格局如下:
Access-Control-Expose-Headers: <header-name>[, <header-name>]*
例如:
Access-Control-Expose-Headers: Custom-Header1, Custom-Header2
下面的例子将向客户端裸露Custom-Header1, Custom-Header2两个header,客户端能够获取到这两个header的值。
- Access-Control-Max-Age
Access-Control-Max-Age示意preflight request的申请后果将会被缓存多久,其格局如下:
Access-Control-Max-Age: <delta-seconds>
delta-seconds是以秒为单位。
- Access-Control-Allow-Credentials
这个字段用来示意服务器端是否承受客户端带有credentials字段的申请。如果用在preflight申请中,则示意后续的实在申请是否反对credentials,其格局如下:
Access-Control-Allow-Credentials: true
- Access-Control-Allow-Methods
这个字段示意拜访资源容许的办法,次要用在preflight request中。其格局如下:
Access-Control-Allow-Methods: <method>[, <method>]*
- Access-Control-Allow-Headers
用在preflight request中,示意真正可能被用来做申请的header字段,其格局如下:
Access-Control-Allow-Headers: <header-name>[, <header-name>]*
有了CORS协定的基本概念之后,咱们就能够开始应用CORS来构建跨域资源拜访了。
根本CORS
先来看一个最根本的CORS申请,比方当初咱们的网站是http://www.flydean.com,在该网站中的某个页面中,咱们心愿获取到https://google.com/data/dataA,那么咱们能够编写的JS代码如下:
const xhr = new XMLHttpRequest();const url = 'https://google.com/data/dataA';xhr.open('GET', url);xhr.onreadystatechange = someHandler;xhr.send();
该申请是一个最根本的CORS申请,咱们看下客户端发送的申请蕴含哪些数据:
GET /data/dataA HTTP/1.1Host: google.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateConnection: keep-aliveOrigin: http://www.flydean.com
这个申请跟CORS无关的就是Origin,示意申请的源域是http://www.flydean.com。
可能的返回后果如下:
HTTP/1.1 200 OKDate: Mon, 01 May 2021 00:23:53 GMTServer: Apache/2Access-Control-Allow-Origin: *Keep-Alive: timeout=2, max=100Connection: Keep-AliveTransfer-Encoding: chunkedContent-Type: application/xml[…Data…]
下面的返回后果要留神的是Access-Control-Allow-Origin: *,示意服务器容许所有的Origin申请。
Preflighted requests
下面的例子是一个最根本的申请,客户端间接向服务器端申请资源。接下来咱们看一个Preflighted requests的例子,Preflighted requests的申请分两局部,第一局部是申请判断,第二局部才是真正的申请。
留神,GET申请是不会发送preflighted的。
什么时候会发送Preflighted requests呢?
当客户端发送OPTIONS办法给服务器的时候,为了平安起见,因为服务器并不一定可能承受这些OPTIONS的办法,所以客户端须要首先发送一个
preflighted requests,期待服务器响应,等服务器确认之后,再发送实在的申请。咱们举一个例子。
const xhr = new XMLHttpRequest();xhr.open('POST', 'https://google.com/data/dataA');flydeanxhr.setRequestHeader('cust-head', 'www.flydean.com');xhr.setRequestHeader('Content-Type', 'application/xml');xhr.onreadystatechange = handler;xhr.send('<site>www.flydean.com</site>');
上例中,咱们向服务器端发送了一个POST申请,在这个申请中咱们增加了一个自定义的header:cust-head。因为这个header并不是HTTP1.1中规范的header,所以须要发送一个Preflighted requests先。
OPTIONS /data/dataA HTTP/1.1Host: google.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateConnection: keep-aliveOrigin: http://www.flydean.comAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: cust-head, Content-Type
申请中增加了Access-Control-Request-Method和Access-Control-Request-Headers这两个多进去的字段。
失去的服务器响应如下:
HTTP/1.1 204 No ContentDate: Mon, 01 May 2021 01:15:39 GMTServer: Apache/2Access-Control-Allow-Origin: http://www.flydean.comAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: cust-head, Content-TypeAccess-Control-Max-Age: 86400Vary: Accept-Encoding, OriginKeep-Alive: timeout=2, max=100Connection: Keep-Alive
响应中返回了Access-Control-Allow-Origin,Access-Control-Allow-Methods和Access-Control-Allow-Headers。
当客户端收到服务器的响应之后,发现配后续的申请,就能够持续发送实在的申请了:
POST /data/dataA HTTP/1.1Host: google.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateConnection: keep-alivecust-head: www.flydean.comContent-Type: text/xml; charset=UTF-8Referer: http://www.flydean.com/index.htmlContent-Length: 55Origin: http://www.flydean.comPragma: no-cacheCache-Control: no-cache<site>www.flydean.com</site>
在实在的申请中,咱们不须要再发送Access-Control-Request*头标记了,只须要发送实在的申请数据即可。
最初,咱们失去server端的响应:
HTTP/1.1 200 OKDate: Mon, 01 May 2021 01:15:40 GMTServer: Apache/2Access-Control-Allow-Origin: http://www.flydean.comVary: Accept-Encoding, OriginContent-Encoding: gzipContent-Length: 235Keep-Alive: timeout=2, max=99Connection: Keep-AliveContent-Type: text/plain[Some data]
带认证的申请
有时候,咱们须要拜访的资源须要带认证信息,这些认证信息是通过HTTP cookies来进行传输的,然而对于浏览器来说,默认状况下是不会进行认证的。要想进行认证,必须设置特定的标记:
const invocation = new XMLHttpRequest();const url = 'https://google.com/data/dataA';function corscall() { if (invocation) { invocation.open('GET', url, true); invocation.withCredentials = true; invocation.onreadystatechange = handler; invocation.send(); }}
下面的例子中,咱们设置了withCredentials flag,示意这是一个带认证的申请。
其对应的申请如下:
GET data/dataA HTTP/1.1Host: google.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateConnection: keep-aliveReferer: http://www.flydean.com/index.htmlOrigin: http://www.flydean.comCookie: name=flydean
申请中咱们带上了Cookie,服务器对应的响应如下:
HTTP/1.1 200 OKDate: Mon, 01 May 2021 01:34:52 GMTServer: Apache/2Access-Control-Allow-Origin: http://www.flydean.comAccess-Control-Allow-Credentials: trueCache-Control: no-cachePragma: no-cacheSet-Cookie: name=flydean; expires=Wed, 31-May-2021 01:34:53 GMTVary: Accept-Encoding, OriginContent-Encoding: gzipContent-Length: 106Keep-Alive: timeout=2, max=100Connection: Keep-AliveContent-Type: text/plain[text/plain payload]
服务器返回了Access-Control-Allow-Credentials: true,示意服务器接管credentials认证,并且返回了Set-Cookie选项对客户端的cookie进行更新。
要留神的是如果服务器反对credentials,那么返回的Access-Control-Allow-Origin,Access-Control-Allow-Headers和Access-Control-Allow-Methods的值都不能是*。
总结
本文简略介绍了HTTP协定中的CORS协定,要留神的是CORS实际上是HTTP申请头和响应头之间的交互。
本文已收录于 http://www.flydean.com/cors/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!