跨域資源共享(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 的某個特殊標誌位。
- 請求配置需新增 withCredentials 標誌設定為 true
- 伺服器端的響應中設定響應頭 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>]*