CORS 跨域資源共享

AlenQi發表於2018-02-12

Catagory

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

Foreword

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

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

CORS Principle

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

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

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

Origin: http://www.alenqi.com

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

Access-Control-Allow-Origin: Origin: http://www.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 跨域資源共享

  • 預請求

    • 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請求。

CORS 跨域資源共享

  • 帶憑據的請求

    • 預設情況下,跨域請求不提供(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: <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
    CORS 跨域資源共享
  • mobile
    CORS 跨域資源共享

相關文章