跨域理解

本文整理了一些有關跨域的基礎知識和細節問題。javascript

什麼是跨域

我的一句話解釋:若是 url Aurl B 不一樣源,那麼頁面A不能獲取頁面B的資源。這裏有兩個關鍵詞:url同源,瀏覽器的同源策略就是針對兩個url,它們知足如下三個條件,纔是同源:html

  • 協議相同
  • 域名相同
  • 端口相同

https://www.baidu.com/java

這裏面有兩個細節須要說起,域名是包括///的全部部分www.baidu.com;沒有指定端口號默認是:80端口。跨域

若是一個頁面中的JS能夠任意發起http跨域獲取其餘頁面上的資源,這是一件很可怕的事,舉個例子:瀏覽器

  1. 張三訪問了某銀行網站 abank.com,服務器端驗證後會在響應頭中加入Set-Cookie字段,而後下次張三再發起請求,瀏覽器會自動將cookie附加在HTTP請求的首部字段Cookie中,服務器端就知道張三已經登陸過了。
  2. 張三接着訪問了危險頁面 danger.com,這個頁面中寫了一些Ajax,它發起請求訪問abank.com,這一行爲用戶不能察覺,若是能夠跨域發起請求,那麼瀏覽器一樣會自動將cookie附加在HTTP請求的首部字段Cookie中,這樣這個危險危險網站就登錄了張三的銀行帳戶。

因此用同源策略來限制跨域是必須的。服務器

這裏我思考了一個問題,在瀏覽器地址欄裏輸入url爲何沒有出現跨域問題?
要明確,同源策略是瀏覽器的行爲,在地址欄中輸入url是用戶主觀行爲,因此瀏覽器是不判爲跨域的。那麼同源限制的對象實際上是頁面中發起http請求的js。那同源限制的行爲有哪些呢?有如下三種:cookie

  • Cookie、LocalStorage 和 IndexDB 沒法讀取。
  • DOM 沒法得到。
  • AJAX 請求不能發送。

接下來講說規避跨域限制的集中方式app

針對AJAX的跨域

JSONP

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

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)。
只要同時知足如下兩大條件,就屬於簡單請求。

  1. 請求方法是如下三種方法之一:

    • HEAD
    • GET
    • POST
  2. HTTP的頭信息不超出如下幾種字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不一樣時知足上面兩個條件,就屬於非簡單請求。

簡單請求

簡單請求的CORS分爲如下幾步:

  1. 瀏覽器發現AJAX請求跨源,在請求頭中添加Origin字段,它的值是http請求的源url;
  2. 服務器根據Origin的值決定是否贊成此次請求;
  3. 若是Origin指定的域名在服務器許可範圍內,則會在報頭中添加如下三個字段:

    • Access-Control-Allow-Origin
      該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求.
    • Access-Control-Allow-Credentials
      該字段可選。它的值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器,另外一方面,開發者必須在AJAX請求中打開withCredentials屬性。這個值也只能設爲true,若是服務器不要瀏覽器發送Cookie,刪除該字段便可。
    • Access-Control-Expose-Headers
      該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段。
  4. 若是若是Origin指定的域名不在服務器許可範圍內,響應頭中沒有Access-Control-Allow-Origin,瀏覽器就會拋出錯誤,中斷這個http請求。

非簡單請求

簡單請求的CORS分爲如下幾步:

  1. 瀏覽器在正式請求以前,先發送一個預檢請求,預檢請求的請求方法是OPTIONS,請求頭中帶如下三個字段:

    • Access-Control-Request-Method
      該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法
    • Access-Control-Request-Headers
      可選,該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段
  2. 若是服務器端預檢不經過,瀏覽器報錯;若是經過,返回的響應頭中包含如下幾個字段:

    • Access-Control-Allow-Origin
      必定包含
    • Access-Control-Allow-Methods
      必定包含,它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。
    • Access-Control-Allow-Credentials
      可選
    • Access-Control-Max-Age
      可選,用來指定本次預檢請求的有效期,單位爲秒,未過時的話不用再發送預檢請求。
  3. 一旦服務器經過了"預檢"請求,之後每次瀏覽器正常的CORS請求,就都跟簡單請求同樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。

WebSocket

WebSocket是一種通訊協議,使用ws://(非加密)和wss://(加密)做爲協議前綴。該協議不實行同源政策,只要服務器支持,就能夠經過它進行跨源通訊。

針對Cookie

Cookie 是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。可是,兩個網頁一級域名相同,只是二級域名不一樣,瀏覽器容許經過設置document.domain共享 Cookie。

舉例來講,A網頁是http://w1.example.com/a.html,B網頁是http://w2.example.com/b.html,那麼只要在兩個網頁的腳本中設置的document.domain='example.com',兩個網頁就能夠共享Cookie。

針對iframe

若是兩個網頁不一樣源,就沒法拿到對方的DOM。典型的例子是iframe窗口和window.open方法打開的窗口,它們與父窗口沒法通訊。對於徹底不一樣源的網站,目前有三種方法,能夠解決跨域窗口的通訊問題:

  • 片斷識別符(fragment identifier)
  • window.name
  • 跨文檔通訊API(Cross-document messaging),HTML5中的window.postMessage

參考文章:
瀏覽器同源政策及其規避方法
跨域資源共享 CORS 詳解

相關文章
相關標籤/搜索