本文整理了一些有關跨域的基礎知識和細節問題。javascript
我的一句話解釋:若是 url
A
與 url
B
不一樣源,那麼頁面A
不能獲取頁面B
的資源。這裏有兩個關鍵詞:url
和 同源
,瀏覽器的同源策略就是針對兩個url
,它們知足如下三個條件,纔是同源:html
這裏面有兩個細節須要說起,域名是包括//
到/
的全部部分www.baidu.com
;沒有指定端口號默認是:80
端口。跨域
若是一個頁面中的JS
能夠任意發起http
跨域獲取其餘頁面上的資源,這是一件很可怕的事,舉個例子:瀏覽器
abank.com
,服務器端驗證後會在響應頭中加入Set-Cookie字段,而後下次張三再發起請求,瀏覽器會自動將cookie附加在HTTP請求的首部字段Cookie中,服務器端就知道張三已經登陸過了。danger.com
,這個頁面中寫了一些Ajax
,它發起請求訪問abank.com
,這一行爲用戶不能察覺,若是能夠跨域發起請求,那麼瀏覽器一樣會自動將cookie附加在HTTP請求的首部字段Cookie中,這樣這個危險危險網站就登錄了張三的銀行帳戶。因此用同源策略來限制跨域是必須的。服務器
這裏我思考了一個問題,在瀏覽器地址欄裏輸入url
爲何沒有出現跨域問題?
要明確,同源策略是瀏覽器的行爲,在地址欄中輸入url
是用戶主觀行爲,因此瀏覽器是不判爲跨域的。那麼同源限制的對象實際上是頁面中發起http請求的js。那同源限制的行爲有哪些呢?有如下三種:cookie
接下來講說規避跨域限制的集中方式app
JSONP利用的的是:<script>、<img>這些標籤下載url中的資源是沒有跨域限制的。
它的基本作法是:網頁添加一個<script>元素,向服務器請求 JSON 數據,這種作法不受同源政策限制;服務器收到請求後,將JSON放在一個指定名字的回調函數裏傳回來。因此,顧名思義,JSONP = JSON with padding
:填充式 JSON,其實就是服務器端把 JSON 做爲回調函數的參數,返回這個回調函數。cors
<script type="text/javascript"> function foo(data) { console.log('Your public IP address is: ' + data.ip); }; <script> <script type="text/javascript" src="http://example.com/ip?callback=foo"></script>
上面的script標籤向服務器example.com
發出請求,該請求有一個查詢字符串callback參數,用來指定回調函數的名字;服務器發現請求中有callback參數,就會將JSON做爲指定回調函數的參數,回調函數做爲腳本返回;腳本返回後,會直接做爲代碼運行,這時,只要瀏覽器定義了foo
函數,該函數就會當即調用。dom
JSONP的缺點在於只能發GET請求。
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。相比JSONP只能發GET請求,CORS容許任何類型的請求。
CORS須要瀏覽器和服務器同時支持。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。
所以,實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。
阮一峯老師關於CORS的文章很是細緻,跨域資源共享 CORS 詳解,這裏僅僅梳理下提綱備查。
瀏覽器將CORS請求分紅兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
只要同時知足如下兩大條件,就屬於簡單請求。
請求方法是如下三種方法之一:
HTTP的頭信息不超出如下幾種字段:
凡是不一樣時知足上面兩個條件,就屬於非簡單請求。
簡單請求的CORS分爲如下幾步:
若是Origin指定的域名在服務器許可範圍內,則會在報頭中添加如下三個字段:
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
true
,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器,另外一方面,開發者必須在AJAX請求中打開withCredentials
屬性。這個值也只能設爲true
,若是服務器不要瀏覽器發送Cookie,刪除該字段便可。Access-Control-Expose-Headers
XMLHttpRequest
對象的getResponseHeader()
方法只能拿到6個基本字段。Origin
指定的域名不在服務器許可範圍內,響應頭中沒有Access-Control-Allow-Origin
,瀏覽器就會拋出錯誤,中斷這個http請求。簡單請求的CORS分爲如下幾步:
瀏覽器在正式請求以前,先發送一個預檢請求,預檢請求的請求方法是OPTIONS
,請求頭中帶如下三個字段:
Access-Control-Request-Method
Access-Control-Request-Headers
若是服務器端預檢不經過,瀏覽器報錯;若是經過,返回的響應頭中包含如下幾個字段:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Credentials
Access-Control-Max-Age
Origin
頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin
頭信息字段。WebSocket
是一種通訊協議,使用ws://
(非加密)和wss://
(加密)做爲協議前綴。該協議不實行同源政策,只要服務器支持,就能夠經過它進行跨源通訊。
Cookie 是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。可是,兩個網頁一級域名相同,只是二級域名不一樣,瀏覽器容許經過設置document.domain
共享 Cookie。
舉例來講,A網頁是http://w1.example.com/a.html,B網頁是http://w2.example.com/b.html,那麼只要在兩個網頁的腳本中設置的document.domain='example.com',兩個網頁就能夠共享Cookie。
若是兩個網頁不一樣源,就沒法拿到對方的DOM。典型的例子是iframe
窗口和window.open
方法打開的窗口,它們與父窗口沒法通訊。對於徹底不一樣源的網站,目前有三種方法,能夠解決跨域窗口的通訊問題:
window.postMessage