本文主要涉及三種跨域方法:JSONP、CORS、postMessage。javascript
Q:爲何會出現跨域問題?
A:出於瀏覽器的同源策略限制,瀏覽器會拒絕跨域請求。
*注:嚴格的說,瀏覽器並非拒絕全部的跨域請求,實際上拒絕的是跨域的讀操做。瀏覽器的同源限制策略是這樣執行的:php
- 一般瀏覽器容許進行跨域寫操做(Cross-origin writes),如連接,重定向;
- 一般瀏覽器容許跨域資源嵌入(Cross-origin embedding),如 img、script 標籤;
- 一般瀏覽器不容許跨域讀操做(Cross-origin reads)。*
Q:什麼狀況纔算做跨域?
A:非同源請求,均爲跨域。名詞解釋:同源 —— 若是兩個頁面擁有相同的協議(protocol),端口(port)和主機(host),那麼這兩個頁面就屬於同一個源(origin)。
htmlQ:爲何有跨域需求?
A:場景 —— 工程服務化後,不一樣職責的服務分散在不一樣的工程中,每每這些工程的域名是不一樣的,但一個需求可能須要對應到多個服務,這時便須要調用不一樣服務的接口,所以會出現跨域。html5
一般,最經常使用的跨域方式有如下三種:JSONP、CORS、postMessage。java
單純地爲了實現跨域請求而創造的一個 trick。
【實現原理】
雖然由於同源策略的影響,不能經過XMLHttpRequest請求不一樣域上的數據(Cross-origin reads)。可是,在頁面上引入不一樣域上的js腳本文件倒是能夠的(Cross-origin embedding)。所以在js文件載入完畢以後,觸發回調,能夠將須要的data做爲參數傳入。
【實現方式(需先後端配合)】json
<script type="text/javascript">
function dosomething(data){
//處理得到的數據
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>複製代碼
<?php $callback = $_GET['callback'];//獲得回調函數名 $data = array('a','b','c');//要返回的數據 echo $callback.'('.json_encode($data).')';//輸出 ?>複製代碼
【JSONP的優缺點】
優勢:兼容性好(兼容低版本IE)
缺點:1.JSONP只支持GET請求; 2.XMLHttpRequest相對於JSONP有着更好的錯誤處理機制後端
CORS 是W3C 推薦的一種新的官方方案,能使服務器支持 XMLHttpRequest 的跨域請求。CORS 實現起來很是方便,只須要增長一些 HTTP 頭,讓服務器能聲明容許的訪問來源。跨域
值得注意的是,一般使用CORS時,異步請求會被分爲簡單請求和非簡單請求,非簡單請求的區別是會先發一次預檢請求。
【簡單請求】
使用下列方法之一且沒有人爲設置對 CORS 安全的首部字段集合以外的其餘首部字段:瀏覽器
- 僅當POST方法的Content-Type值等於下列之一纔算做簡單請求
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded複製代碼
請求報文:安全
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複製代碼
請求報文的第10行:Origin: foo.example 代表該請求來源於 foo.exmaple。
響應報文:
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]複製代碼
響應報文的第4行:Access-Control-Allow-Origin: * 代表該資源能夠被任意外域訪問。
【非簡單請求】
發送真正請求前會先發送預檢請求,如圖所示:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER複製代碼
預檢請求的Request中的Access-Control-Request-Method: POST,是告訴服務器,以後的實際請求將使用POST方式。
Access-Control-Request-Headers 是告訴服務器,實際請求將攜帶兩個自定義請求首部字段:X-PINGOTHER 與 Content-Type。服務器據此決定,該實際請求是否被容許
預檢請求的Response中的
Access-Control-Allow-Origin: foo.example // 標識可接受的跨域請求源;
Access-Control-Allow-Methods: POST, GET, OPTIONS //標識可接受的跨域請求方法,如GET、POST、OPTIONS;
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //標識可接受的跨域請求自定義頭;
Access-Control-Max-Age: 86400。 //標識本次預請求的有效時間(秒),期間內無需再發送預請求;
XMLHttpRequest 請求能夠發送憑證請求(HTTP Cookies 和驗證信息),一般不會跨域發送憑證信息,但也有一些狀況須要打通不一樣的登陸態,所以若是要發送憑證信息,須要設置 XMLHttpRequest 的某個特殊標誌位。好比下面代碼,能夠把 XMLHttpRequest 的 withCredentials 設置爲 true,這樣瀏覽器就能跨域發送憑證信息。
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;複製代碼
服務端返回的響應頭中的 Access-Control-Allow-Credentials 字段存在且爲 true 時,瀏覽器纔會將響應結果傳遞給客戶端程序。另外,Access-Control-Allow-Origin 必須指定請求源的域名,不然響應失敗。
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://foo.com Access-Control-Allow-Credentials: true Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain複製代碼
以下圖所示爲附帶身份憑證的請求流程圖:
window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
otherWindow.postMessage(message, targetOrigin, [transfer]);複製代碼