標籤:

CORS 跨域資源共享

Catagory

  1. Foreword
  2. CORS Principle
  3. 三個訪問控制場景
    1. 簡單請求
    2. 預請求
    3. 帶憑據的請求
  4. HTTP請求頭
  5. HTTP響應頭
  6. IE對CORS的實現
  7. 瀏覽器的支持

Foreword

由於同源策略限制從一個源載入的文檔或腳本與來自另一個源的資源進行交互。在web開發中跨域是難免的問題,或是開發時的跨域,或是線上資源請求的跨域。我們可以使用「CORS」允許跨院訪問。

先簡單說下跨域,當一個資源請求一個其它域名或者另外一個埠的資源時會產生一個跨域HTTP請求(cross-origin HTTP request)。為了訪問資源的可靠信,會有同源策略的限制,瀏覽器會攔截跨域請求的返回結果,有些瀏覽器會更加嚴格,不允許從HTTPS的域跨域訪問HTTP,如Chrome和Firefox,這些瀏覽器在請求還未發出的時候就會攔截請求。

CORS Principle

資源請求的響應

CORS(Cross-origin resource sharing,跨域資源共享)是W3C的一個工作草案,定義了在必須訪問跨域資源時,瀏覽器與伺服器應該如何溝通。CORS背後的基本思想,就是使用自定義的HTTP請求頭部,讓瀏覽器和伺服器進行溝通,從而決定請求或響應是應該成功,還是失敗。

這個規範是對針對API容器的,比如 XMLHttpRequest 或者 Fetch

比如一個簡單的GET或者POST請求,它沒有自定義的頭部,而主題內容是text/plain。在發送請求時,需要給它附加一個額外的Oringin頭部,其中包含請求頁面的源信息(協議,域名和埠),以便伺服器根據這個頭部消息來決定是否給予響應。下面是Oringin頭部的一個示例:

Origin: alenqi.com

如果伺服器認為這個請求可以接受,就在Access-Control-Allow-Origin頭部中返回相同的源信息。示例:

Access-Control-Allow-Origin: Origin: alenqi.com

如果沒有這個頭部,或者有這個頭部但是源信息不匹配,瀏覽器就會駁回請求。正常情況下,瀏覽器會處理請求。注意,這裡的請求和響應都不包含cookie信息。

CORS需要瀏覽器和伺服器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。

瀏覽器會自動判斷如果跨域,自動會添加一些附加的頭部信息,還有可能發送預請求。所以,如果服務端實現了響應CORS的介面,就可以跨域訪問。

三個訪問控制場景

  • 簡單請求
    • 一些請求不會觸發 CORS preflight,而這部分在本文中被稱為「簡單請求」。滿足下述條件的就是「簡單請求」:
    • 只允許下列方法:
      • GET
      • POST
      • HEAD
    • 除了用戶代理自動設置的頭部外,唯一允許人工設置的頭部是 Fetch 規範定義的「 CORS-safelisted request-header」,如下:
      • Accept
      • Accept-Language
      • Content-Language
      • Last-Event-ID
      • Content-Type (but note the additional requirements below)
    • 允許的 Content-Type 值有:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • 一個簡單的CORS頭處理的跨域請求

返回頭的「*」代表該資源跨域在跨域行為里可以被任意站點訪問,可以設定為指定的域名

  • 預請求
    • CORS通過一種叫做preflighted Requests的透明伺服器驗證機制支持開發人員使用自定義的頭部,「預請求」要求必須先發送一個 OPTIONS 方法請求給目的站點,來查明這個跨站請求對於目的站點是不是安全的可接受的。這樣做,是因為跨站請求可能會對目的站點的數據產生影響。 當請求具備以下條件,就會被當成預請求處理:
    • 除了下列方法以外方法的請求:
      • GET
      • POST
      • HEAD
    • 除了用戶代理自動設置的頭部外,不包括一下的頭部信息,如下:
      • Accept
      • Accept-Language
      • Content-Language
      • Last-Event-ID - Content-Type (but note the additional requirements below)
    • Content-Type 值有除了一下之外的:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • 簡而言之,就是非簡單請求的請求就會發送預請求。
    • 發送這個請求之後,伺服器可以決定是否允許這種類型的請求。伺服器通過在響應中發送頭部和瀏覽器進行溝通。Preflight請求結束後,結果將按照響應頭中指定的時間緩存起來。而為此付出的代價只是第一次發送非簡單請求時多一次HTTP請求。

一個「預請求」形式的跨域請求

  • 帶憑據的請求
    • 默認情況下,跨域請求不提供(cookie,HTTP認證及客戶端SSL證明等)。通過將withCredentials屬性設置為true,可以指定某個請求應該發送憑據。
    • 如果發送的是帶憑據的請求,但是伺服器的響應中沒有包含這個頭部,那麼瀏覽器就不會把響應交給JavaScript(於是,responseText中將是空字元串,status的值為0,而且會調用onerror()事件處理程序。另外,伺服器還可以在Preflight響應中發送這個HTTP頭部,表示允許源發送帶憑據的請求。

HTTP請求頭

  • 發送請求的域

Origin: <origin>

  • 請求自身使用的方法

Access-Control-Request-Method: <method>

  • (可選)自定義的頭部信息,多個頭部逗號分隔

Access-Control-Request-Headers: <field-name>[, <field-name>]*

HTTP響應頭

  • 與簡單請求的相同

Access-Control-Allow-Origin: | *

  • 設置瀏覽器允許訪問的伺服器的頭信息的白名單

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

  • 應該將這個預請求緩存多長時間(以秒表示)

Access-Control-Max-Age: <delta-seconds>

  • 是否為帶憑證的請求

Access-Control-Allow-Credentials: true | false

  • 允許的方法,多個方法以逗號分隔

Access-Control-Allow-Methods: <method>[, <method>]*

  • 允許的頭部,多個頭部以逗號分隔

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

IE對CORS的實現

  • 微軟在IE8中引入了XDR(XDomainRequest)類型。這個對象與XHR類似,但能實現安全可靠的跨域通信。XDR對象的安全機制部分實現了W3C的CORS規範。下面是XDR與XHR的一些不同之處:
    • cookie不會隨請求發送,也不會隨響應返回
    • 只能設置請求頭不信息中的Content-Type欄位
    • 不能訪問響應頭部信息
    • 只支持GET和POST請求
  • 好消息是對於CORS在IE 10中有完整的實現

瀏覽器的支持

  • 拋開瀏覽器談HTTP相關規範或技術都是耍流氓。
  • Desktop

  • mobile

推薦閱讀:

跨域的那些事兒
iframe 完全跨域,就是不同域名不同伺服器之間的跨域?JS 如何做到
前後端分離的情況下, 跨域問題有沒有好的解決方案?
由Request Method:OPTIONS初窺CORS

TAG:跨域 |