關於跨域的深入理解

zifeiyu發表於2019-04-10

跨域資源共享(cros)是一種機制它使用額外的 HTTP 頭來告訴瀏覽器 讓執行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。

(即使是同一個域不同埠的請求也算是跨域行為)

實際場景

這裡,我們使用三個場景來解釋跨域資源共享機制的工作原理。這些例子都使用 XMLHttpRequest 物件。(對於fetch請求也是大致相同的機理)

簡單請求

某些請求不會觸發 CORS 預檢請求。本文稱這樣的請求為“簡單請求”,請注意,該術語並不屬於 Fetch (其中定義了 CORS)規範。若請求滿足所有下述條件,則該請求可視為“簡單請求”:

  • 使用下列方法之一:
    • GET
    • HEAD
    • POST
  • Fetch 規範定義了對 CORS 安全的首部欄位集合,不得人為設定該集合之外的其他首部欄位。該集合為:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意額外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值僅限於下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  • 請求中的任意XMLHttpRequestUpload 物件均沒有註冊任何事件監聽器;XMLHttpRequestUpload 物件可以使用 XMLHttpRequest.upload 屬性訪問。
  • 請求中沒有使用 ReadableStream 物件。

預檢請求

與前述簡單請求不同,“需預檢的請求”要求必須首先使用 OPTIONS 方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響。

當請求滿足下述任一條件時,即應首先傳送預檢請求:

  • 使用了下面任一 HTTP 方法:
    • PUT
    • DELETE
    • CONNECT
    • OPTIONS
    • TRACE
    • PATCH
  • 人為設定了對 CORS 安全的首部欄位集合之外的其他首部欄位。該集合為:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意額外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值不屬於下列之一:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 請求中的XMLHttpRequestUpload 物件註冊了任意多個事件監聽器。
  • 請求中使用了ReadableStream物件。

附帶身份憑證的請求

Fetch 與 CORS 的一個有趣的特性是,可以基於 HTTP cookies 和 HTTP 認證資訊傳送身份憑證。一般而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會傳送身份憑證資訊。如果要傳送憑證資訊,需要設定 XMLHttpRequest 的某個特殊標誌位。

  1. 請求配置需新增 withCredentials 標誌設定為 true
  2. 伺服器端的響應中設定響應頭 Access-Control-Allow-Credentials: true

!!!!

附帶身份憑證的請求與萬用字元

對於附帶身份憑證的請求,伺服器不得設定 Access-Control-Allow-Origin 的值為“*”。

這是因為請求的首部中攜帶了 Cookie 資訊,如果 Access-Control-Allow-Origin 的值為“*”,請求將會失敗。而將 Access-Control-Allow-Origin 的值設定為 foo.example,則請求將成功執行。

另外,響應首部中也攜帶了 Set-Cookie 欄位,嘗試對 Cookie 進行修改。如果操作失敗,將會丟擲異常。

響應首部欄位

響應首部的欄位是需要手動設定的。

Access-Control-Allow-Origin

響應首部中可以攜帶一個 Access-Control-Allow-Origin 欄位,其語法如下: Access-Control-Allow-Origin: <origin> | *

Access-Control-Expose-Headers

Access-Control-Expose-Headers 頭讓伺服器把允許瀏覽器訪問的頭放入白名單,例如: Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

Access-Control-Max-Age

Access-Control-Max-Age 頭指定了preflight請求的結果能夠被快取多久 Access-Control-Max-Age: <delta-seconds>

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials 頭指定了當瀏覽器的credentials設定為true時是否允許瀏覽器讀取response的內容。當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials。請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該欄位,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁 Access-Control-Allow-Credentials: true

Access-Control-Allow-Methods

Access-Control-Allow-Methods 首部欄位用於預檢請求的響應。其指明瞭實際請求所允許使用的 HTTP 方法。 Access-Control-Allow-Methods: <method>[, <method>]*

Access-Control-Allow-Headers

Access-Control-Allow-Headers 首部欄位用於預檢請求的響應。其指明瞭實際請求中允許攜帶的首部欄位。 Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP 請求首部欄位

本節列出了可用於發起跨域請求的首部欄位。請注意,這些首部欄位無須手動設定。 當開發者使用 XMLHttpRequest 物件發起跨域請求時,它們已經被設定就緒。

Origin

Origin 首部欄位表明預檢請求或實際請求的源站 Origin: <origin>

Access-Control-Request-Method

Access-Control-Request-Method 首部欄位用於預檢請求。其作用是,將實際請求所使用的 HTTP 方法告訴伺服器。 Access-Control-Request-Method: <method>

Access-Control-Request-Headers

Access-Control-Request-Headers 首部欄位用於預檢請求。其作用是,將實際請求所攜帶的首部欄位告訴伺服器。 Access-Control-Request-Headers: <field-name>[, <field-name>]*

相關文章