前端和後臺請求交互,若請求者(Client)和資源者響應者(Server)處在不一樣的域名,會發生跨域請求,不一樣域是指HTTP協議、域名、端口有一個或全都不相同。出於安全考慮瀏覽器限制了從腳本內發起的資源跨域請求,CORS是解決跨域請求的一種機制。html
CORS全稱「跨域資源共享」,它是一種機制經過添加額外的HTTP頭部告訴瀏覽器,容許origin上的web應用訪問不一樣源服務器資源,能夠在XMLHttpRequest、Fetch中使用CORS發起跨域請求。前端
對於前端開發使用CORS發起跨域請求不須要額外工做,當請求發生時瀏覽器會自動設置相應的HTTP頭部信息(下面介紹道),服務器端須要作相應的頭部設置來控制跨域權限。web
對於使用CORS發起跨域請求可分爲簡單請求和非簡請求兩種,簡單請求不會觸發OPTIONS預檢請求,可直接發起跨域請求,非簡單請求會觸發OPTIONS預檢請求,向服務器發起一些詢問信息,例如:是否容許此次跨域請求、告知用什麼請求方法、是否支持在HTTP頭裏攜帶自定義字段。跨域
簡單請求可直接向服務器請求,只要服務器贊成此次跨域求,瀏覽器就能獲取到服務器返回的資源,在請求中只要同時知足如下條件就屬於簡單請求。瀏覽器
a. 使用GET、POST、HEAD 三者之一的請求方法。緩存
b. 不得設置Accept 、 Accept-Language、 Content-Language,Content-Type以外的頭部信息集合字段,對於Content-Type的值不能設置text/plain, multipart/form-data, application/x-www-form-urlencode以外的 值。安全
c. 請求中的任意XMLHttpRequestupload對象均未設置任何監聽器。bash
d. 請求中沒有使用 ReadableStream 對象。服務器
var request = new XMLHttpRequest()
var url = 'http://bar.other/resources/public-data/'
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
//發起請求
callOtherDomain()
複製代碼
請求發出時Client與Server間經過HTTP頭部字段處理跨域權限。cookie
1. GET /resources/public-data/ HTTP/1.1
2. Host: bar.other
3. User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US;
4. rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
5. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
6. Accept-Language: en-us,en;q=0.5
7. Accept-Encoding: gzip,deflate
8. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
9. Connection: keep-alive
10. Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
11. Origin: http://foo.example
複製代碼
1. HTTP/1.1 200 OK
2. Date: Mon, 01 Dec 2008 00:23:53 GMT
3. Server: Apache/2.0.61
4. Access-Control-Allow-Origin: *
5. Keep-Alive: timeout=2, max=100
6. Connection: Keep-Alive
7. Transfer-Encoding: chunked
8. Content-Type: application/xml
複製代碼
Client經過Origin字段告訴Server我是來自http://foo.example
的請求,容許我訪問你的資源嗎?Server作出迴應並在響應頭裏帶上Access-Control-Allow-Origin:* 表示我容許全部的外域訪問個人資源。
若是將Access-Control-Allow-Origin值設置成http://foo.example
,表示Server上的資源只容許來自http://foo.example
的源訪問。後臺開發通常會在此添加請求源白名單來控制容許那些請求源訪問服務資源。
若是Origin不是Access-Control-Allow-Origin容許的源,瀏覽器將攔截請求響應內容,沒法獲取到服務器資源。
只要不知足簡單請求的都屬於非簡單請求。非簡單請求會在正式發起資源訪問請求前觸發一個options 預檢請求,options請求不會對服務器形成資源影響。 只要知足下列條件之一就會觸發預檢請求:
a. PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH。
b. 在請求頭中設置Accept Accept-Language、 Content-Language、 Content-Type (須要注意額外的限制)、 DPR、 Downlink、 Save-Data、 Viewport-Width、 Width的字段。
c. Content-Type 的值不屬於application/x-www-form-urlencoded、 multipart/form-data、 text/plain三者之一。
d. 請求中的XMLHttpRequestUpload 對象註冊了任意多個事件監聽器。
e. 請求中使用了ReadableStream對象。
再次借用MSDN中的例子說下
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
// 發起請求
callOtherDomain()
複製代碼
在代碼中能夠看到,咱們人爲的自定義了X-PINGOTHER
請求頭字段(知足非簡單請求b條件),而且Content-Type
被設置application/xml (知足非簡單請求c條件),因此該請求首先會觸發一個options請求。
下面是options請求頭的部分信息:
1.OPTIONS /resources/post-here/ HTTP/1.1
2.Host: bar.other
3.User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
4.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5.Accept-Language: en-us,en;q=0.5
6.Accept-Encoding: gzip,deflate
7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
8.Connection: keep-alive
9.Origin: http://foo.example
10.Access-Control-Request-Method: POST
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type
複製代碼
options 請求告訴Server, 我是來自http://foo.example
的請求,在正式請求時,我會在請求頭中攜帶自定義字段X-PINGOTHER、Conten-Type而且我將經過POST發起請求,你贊成不?如下是用於向Server詢問的頭部信息字段:
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
複製代碼
咱們看下Server對options請求是如何迴應的
1.HTTP/1.1 200 OK
2.Date: Mon, 01 Dec 2008 01:15:39 GMT
2.Server: Apache/2.0.61 (Unix)
3.Access-Control-Allow-Origin: http://foo.example
4.Access-Control-Allow-Methods: POST, GET, OPTIONS
5.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
6.Access-Control-Max-Age: 86400
7.Vary: Accept-Encoding, Origin
8.Content-Encoding: gzip
9.Content-Length: 0
10.Keep-Alive: timeout=2, max=100
11.Connection: Keep-Alive
12.Content-Type: text/plain
複製代碼
從上面的options響應頭裏得知, 首部字段 Access-Control-Allow-Origin代表服務器容許http://foo.example請求源訪問資源。 字段 Access-Control-Allow-Headers 代表服務器容許請求頭中攜帶字段 X-PINGOTHER 與 Content-Type。 Access-Control-Allow-Methods告知容許正式請求使用POST方法。 options請求結束,若是Server贊成訪問,接下來就會發起正式的請求,不然結束請求。
第6行代碼中有一段Access-Control-Max-Age: 86400
信息,這段信息的意思是說在接下來的86400秒內對於同一請求不用再發起options預檢請求。Access-Control-Max-Age單位是秒,由後臺設置但不能超過瀏覽器支持的最大有效時間,不然不會生效。
Fetch 與 CORS 能夠基於 HTTP cookies 和 HTTP 認證信息發送身份憑證。通常而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會發送身份憑證信息。
若是要發送憑證信息,須要設置 XMLHttpRequest 的withCredentials=true,同時Server端也要設置響應的頭部字段Access-Control-Allow-Credentials: true,才能在請求中攜帶身份憑證,不然,瀏覽器會攔截響應內容,不會把它返回給請求發送者。
須要注意,Server若是設置了Access-Control-Allow-Origin:*
同時請求又攜帶了cookies信息,會致使請求失敗。
Origin字段代表預檢請求或實際請求的源站,origin 參數的值爲源站 URI。它不包含任何路徑信息,只是服務器名稱,無論是否爲跨域請求都會發送Origin字段。
Origin: <origin>
複製代碼
Access-Control-Request-Method字段用於預檢請求。其做用是,將實際請求所使用的 HTTP方法告訴服務器。
Access-Control-Request-Method: <method>
複製代碼
Access-Control-Request-Headers字段用於預檢請求。其做用是,將實際請求所攜帶的首部字段告訴服務器。
Access-Control-Request-Headers: <field-name>[, <field-name>]*
複製代碼
Access-Control-Allow-Origin字段,origin 參數的值指定了容許訪問該資源的外域 URI。對於不須要攜帶身份憑證的請求,服務器能夠指定該字段的值爲通配符,表示容許來自全部域的請求。
Access-Control-Allow-Origin: <origin> | *
複製代碼
Access-Control-Expose-Headers字段,設置瀏覽器能夠拿到的頭部字段白名單。 在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,若是要訪問其餘頭,須要服務器設置本響應頭,這就是Access-Control-Expose-Headers的做用。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
複製代碼
Access-Control-Max-Age字段,設置預檢測請求能緩存多久,單位秒。
Access-Control-Max-Age: <delta-seconds>
複製代碼
Access-Control-Allow-Credentials字段,指定了當瀏覽器的credentials設置爲true時是否容許瀏覽器讀取response的內容
Access-Control-Allow-Credentials: true
複製代碼
Access-Control-Allow-Methods字段用於預檢請求的響應。其指明瞭實際請求所容許使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*
複製代碼
Access-Control-Allow-Headers 首部字段用於預檢請求的響應。其指明瞭實際請求中容許攜帶的首部字段。
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
複製代碼
目前除了IE9及如下瀏覽不支持CORS外,其餘瀏覽器基本的已經支持CORS。對於IE9及如下的瀏覽器可經過 XDomainRequest實現。
以上是我對CORS知識的梳理和記錄,由於在以往工做中頻繁遇到跨域問題,思來想去決定記錄下來,爲確保信息的正確性,極大的參照來官方文檔,但願對有須要的小夥伴在處理跨域問題上有所幫助。
聲明:本文的內容參考於MDN官方文檔,包括上面用到的事例代碼也是借用自MDN,如有侵權,請聯繫我刪除相關內容。