主流跨域方式:cors 跨域 && jsonp 跨域
- 不允許跨域的基本原因是:http請求頭中沒有允許跨域有關的引數。解決跨域請求的原理就是在攔截器中攔截請求,請在Http請求頭新增允許跨域的欄位
jsonp 跨域請求
- jsonp的缺點就是隻能採用get請求。
- 為了減輕web伺服器的負載,我們把js、css,img等靜態資源分離到另一臺獨立域名的伺服器上,在html頁面中再通過相應的標籤從不同域名下載入靜態資源,而被瀏覽器允許
- 基於此原理,我們可以通過動態建立script,再請求一個帶參網址實現跨域通訊。
$.ajax({
url: 'http://localhost:1112/getPerson?callback=?',
dataType: 'jsonp',
jsonpCallback: 'person',
success: function(data){
$('.name').html(data.name);
$('.company').html(data.company);
}
})
複製程式碼
cors(cross-origin-resource-share) 跨域資源共享:
cors機制:
- 它使用額外的HTTP頭來告訴瀏覽器,讓執行在一個origin上的web應用被准許訪問來自不同源伺服器上的指定資源。
- 當一個資源從與該資源本身所在的伺服器不同的域/協議/埠請求一個資源的時候,資源會發起一個跨域請求;
舉例:
- 站點http://domain-a.com 的某html頁面通過 src 請求http://domain-b.com/imge.jpg;
- 網路上的許多頁面載入來自不同域的css樣式表/影像/和指令碼等資源;
處於安全原因:
- 瀏覽器限制從指令碼內發起http跨域請求,或者是跨站請求可以正常發起,但是返回結果被瀏覽器攔截了 例如:XMLHttpRequest 和 fetch發起的請求;
- cors機制,允許web應用伺服器進行跨域訪問控制,從而是的跨域訪問資料傳輸得以安全進行;
API遵循同源策略:
- 這意味著使用這些API的Web應用程式,只能從載入應用程式的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。
新增CORS標準:
- 跨域資源共享標準新增了一組 HTTP 首部欄位,允許伺服器宣告哪些源站通過瀏覽器有許可權訪問哪些資源;
- 對那些可能對伺服器資料產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 型別的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。伺服器確認允許之後,才發起實際的 HTTP 請求。
cors舉例:
比如說,假如站點 http://foo.example 的網頁應用想要訪問 http://bar.other 的資源。http://foo.example 的網頁中可能包含類似於下面的 JavaScript 程式碼:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
複製程式碼
簡單請求:
不進行預檢請求的http請求,稱為簡單請求;
Request:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
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
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
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
[XML Data]
複製程式碼
預檢請求:
- “需預檢的請求”要求必須首先使用 OPTIONS 方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。
- "預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響。
預檢請求條件:
1.使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
2.人為設定了對 CORS 安全的首部欄位集合之外的其他首部欄位。該集合為:
Accept
Accept-Language
Content-Language
Content-Type (需要注意額外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
3.Content-Type 的值不屬於下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
4.請求中的XMLHttpRequestUpload 物件註冊了任意多個事件監聽器。
5.請求中使用了ReadableStream物件。
複製程式碼
如下是一個需要執行預檢請求的 HTTP 請求:
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);
}
}
......
複製程式碼
Request:
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
Response:
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain
複製程式碼
預檢請求完成之後,傳送實際請求:
POST /resources/post-here/ HTTP/1.1
Host: bar.other
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
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache
<?xml version="1.0"?><person><name>Arun</name></person>
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some GZIP'd payload]
首部欄位 Access-Control-Max-Age 表明該響應的有效時間為 86400 秒,也就是 24 小時。在有效時間內,瀏覽器無須為同一請求再次發起預檢請求
複製程式碼
附帶身份憑證的請求:
Fetch 與 CORS 的一個有趣的特性是,可以基於 HTTP cookies 和 HTTP 認證資訊傳送身份憑證。一般而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會傳送身份憑證資訊。如果要傳送憑證資訊,需要設定 XMLHttpRequest 的某個特殊標誌位。
本例中,foo.example 的某指令碼向 bar.other 發起一個GET 請求,並設定 Cookies:
var invocation = new XMLHttpRequest(); var url = 'bar.other/resources/c…';
function callOtherDomain(){ if(invocation) { invocation.open('GET', url, true); invocation.withCredentials = true; invocation.onreadystatechange = handler; invocation.send();
} }
- withCredentials 標誌設定為 true,從而向伺服器傳送 Cookies
- 因為這是一個簡單 GET 請求,所以瀏覽器不會對其發起“預檢請求”。但是,如果伺服器端的響應中未攜帶 Access-Control-Allow-Credentials: true ,瀏覽器將不會把響應內容返回給請求的傳送者。
附帶身份憑證的請求與萬用字元
- 附帶身份憑證的請求,伺服器不得設定 Access-Control-Allow-Origin 的值為“*”。
這是因為請求的首部中攜帶了 Cookie 資訊,如果 Access-Control-Allow-Origin 的值為“*”,請求將會失敗。而將 Access-Control-Allow-Origin 的值設定為 foo.example,則請求將成功執行。
HTTP 響應首部欄位
Access-Control-Allow-Origin
響應首部中可以攜帶一個 Access-Control-Allow-Origin 欄位,其語法如下:
Access-Control-Allow-Origin: <origin> | *
複製程式碼
其中:
- origin 引數的值指定了允許訪問該資源的外域 URI。對於不需要攜帶身份憑證的請求,伺服器可以指定該欄位的值為萬用字元,表示允許來自所有域的請求。
例如,下面的欄位值將允許來自 mozilla.com 的請求:
Access-Control-Allow-Origin: http://mozilla.com
複製程式碼
如果服務端指定了具體的域名而非“*”,那麼響應首部中的 Vary 欄位的值必須包含 Origin。這將告訴客戶端:伺服器對不同的源站返回不同的內容。
Access-Control-Allow-Credentials
- Access-Control-Allow-Credentials頭指定了當瀏覽器的credentials設定為true時是否允許瀏覽器讀取response的內容。
- 當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials。請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該欄位,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁。
Access-Control-Allow-Credentials: true
複製程式碼
HTTP 請求首部欄位
Origin
Origin 首部欄位表明預檢請求或實際請求的源站。
Origin: <origin>
複製程式碼
origin 引數的值為源站 URI。它不包含任何路徑資訊,只是伺服器名稱。
Access-Control-Request-Headers
Access-Control-Request-Headers 首部欄位用於預檢請求。其作用是,將實際請求所攜帶的首部欄位告訴伺服器。
Access-Control-Request-Headers: <field-name>[, <field-name>]*
複製程式碼