我們都知道由于同源策略的存在,導(dǎo)致我們在跨域請求數(shù)據(jù)的時候非常的麻煩
首先阻擋我們的所謂"同源"到底是什么呢?
所謂同源就是瀏覽器的一個安全機(jī)制,不同源的客戶端腳本沒有在明確授權(quán)的情況下,不能讀寫對方資源怨绣。
由于存在同源策略的限制,而又有需要跨域的業(yè)務(wù),所以就有啦CORS的出現(xiàn)
我們都知道,jsonp也可以跨域,那為什么還要使用CORS呢
- jsonp只可以使用 GET 方式提交
- 不好調(diào)試,在調(diào)用失敗的時候不會返回任何狀態(tài)碼
- 安全性,萬一假如提供jsonp的服務(wù)存在頁面注入漏洞凹炸,即它返回的javascript的內(nèi)容被人控制的饵溅。那么結(jié)果是什么超燃?所有調(diào)用這個 jsonp的網(wǎng)站都會存在漏洞帖汞。于是無法把危險控制在一個域名下…所以在使用jsonp的時候必須要保證使用的jsonp服務(wù)必須是安全可信的。
開始CORS
CORS 是一個W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)掀潮,他允許瀏覽器向跨源服務(wù)器發(fā)送XMLHttpRequest請求骄呼,從而克服啦 AJAX 只能同源使用的限制
CORS 需要瀏覽器和服務(wù)器同時支持共苛,整個 CORS通信過程,都是瀏覽器自動完成不需要用戶參與蜓萄,對于開發(fā)者來說隅茎,CORS的代碼和正常的 ajax 沒有什么差別,瀏覽器一旦發(fā)現(xiàn)跨域請求嫉沽,就會添加一些附加的頭信息辟犀,
CORS這么好嗎,難道就沒有缺點(diǎn)嘛绸硕?
答案肯定是NO堂竟,目前所有最新瀏覽器都支持該功能,但是萬惡的IE不能低于10
簡單請求和非簡單請求
瀏覽器將CORS請求分成兩類:簡單請求和非簡單請求
1. 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
1. HTTP的頭信息不超出以下幾種字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三個值 `application/x-www-form-urlencoded`玻佩、`multipart/form-data`出嘹、`text/plain`
凡是同時滿足上面兩種情況的就是簡單請求,反之則非簡單請求咬崔,瀏覽器對這兩種請求的處理不一樣
簡單請求
對于簡單請求來說疚漆,瀏覽器之間發(fā)送CORS請求,具體來說就是在頭信息中,增加一個origin字段
來看下例子:
GET /cors? HTTP/1.1
Host: localhost:2333
Connection: keep-alive
Origin: http://localhost:2332
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: */*
Referer: http://localhost:2332/CORS.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
If-None-Match: W/"1-NWoZK3kTsExUV00Ywo1G5jlUKKs"
上面的頭信息中娶聘,Origin字段用來說名本次請求來自哪個源,服務(wù)器根據(jù)這個值,決定是否同意這次請求.
如果Origin指定的源不在允許范圍之內(nèi),服務(wù)器就會返回一個正常的HTTP回應(yīng),然后瀏覽器發(fā)現(xiàn)頭信息中沒有包含Access-Control-Allow-Origin 字段,就知道出錯啦,然后拋出錯誤,反之則會出現(xiàn)這個字段(實(shí)例如下)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
- Access-Control-Allow-Origin 這個字段是必須的,表示接受那些域名的請求(*為所有)
- Access-Control-Allow-Credentials 該字段可選, 表示是否可以發(fā)送cookie
- Access-Control-Expose-Headers 該字段可選,XHMHttpRequest對象的方法只能夠拿到六種字段: Cache-Control闻镶、Content-Language、Content-Type丸升、Expires铆农、Last-Modified、Pragma ,如果想拿到其他的需要使用該字段指定狡耻。
如果你想要連帶Cookie一起發(fā)送,是需要服務(wù)端和客戶端配合的
// 服務(wù)端
Access-Control-Allow-Credentials: true
// 客戶端
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// 但是如果省略withCredentials屬性的設(shè)置,有的瀏覽器還是會發(fā)送cookie的
xhr.withCredentials = false;
非簡單請求
非簡單請求則是不滿足上邊的兩種情況之一,比如請求的方式為 PUT,或者請求頭包含其他的字段
非簡單請求的CORS請求是會在正式通信之前進(jìn)行一次預(yù)檢請求
瀏覽器先詢問服務(wù)器,當(dāng)前網(wǎng)頁所在的域名是否可以請求您的服務(wù)器,以及可以使用那些HTTP動詞和頭信息,只有得到正確的答復(fù),才會進(jìn)行正式的請求
// 前端代碼
var url = 'http://localhost:2333/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
由于上面的代碼使用的是 PUT 方法,并且發(fā)送啦一個自定義頭信息.所以是一個非簡單請求,當(dāng)瀏覽器發(fā)現(xiàn)這是一個非簡單請求的時候,會自動發(fā)出預(yù)檢請求,看看服務(wù)器可不可以接收這種請求,下面是"預(yù)檢"的 HTTP 頭信息
OPTIONS /cors HTTP/1.1
Origin: localhost:2333
Access-Control-Request-Method: PUT // 表示使用的什么HTTP請求方法
Access-Control-Request-Headers: X-Custom-Header // 表示瀏覽器發(fā)送的自定義字段
Host: localhost:2332
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
User-Agent: Mozilla/5.0...
"預(yù)檢"使用的請求方法是 OPTIONS , 表示這個請求使用來詢問的,
預(yù)檢請求后的回應(yīng)
服務(wù)器收到"預(yù)檢"請求以后墩剖,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后夷狰,確認(rèn)允許跨源請求岭皂,就可以做出回應(yīng)
預(yù)檢的響應(yīng)頭:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://localhost:2332 // 表示http://localhost:2332可以訪問數(shù)據(jù)
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
如果瀏覽器否定啦"預(yù)檢"請求,會返回一個正常的HTTP回應(yīng),但是沒有任何CORS的頭相關(guān)信息,這是瀏覽器就認(rèn)定,服務(wù)器不允許此次訪問,從而拋出錯誤
預(yù)檢之后的請求
當(dāng)預(yù)檢請求通過之后發(fā)出正經(jīng)的HTTP請求,還有一個就是一旦通過啦預(yù)檢請求就會,請求的時候就會跟簡單請求,會有一個Origin頭信息字段。
通過預(yù)檢之后的,瀏覽器發(fā)出發(fā)請求
PUT /cors HTTP/1.1
Origin: http://api.bob.com // 通過預(yù)檢之后的請求,會自動帶上Origin字段
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...