HTTP(二)、跨域資源共享(CORS)
2.跨域資源共享(CORS)
跨域簡介
當訪問一個資原始檔時,如果從非該資原始檔所在的伺服器不同域名或者埠處進行訪問時,該資源會發起一個跨域請求。
例如,網站A的地址是http://www.domain-a.com ,該網站中HTML頁面通過 img
標籤中的 src
屬性請求http://www.domain-b.com/static/xxx.png 圖片資源,此時,就發生了跨域請求。
處於安全考慮,瀏覽器限制從指令碼內發起的跨域資源請求。例如,XHR和Fetch的API遵循同源策略。這意味著使用這些API只能從同一個域才能請求資原始檔。
需要注意的是,跨域不一定是瀏覽器限制了發起跨域請求,也可能是跨域請求可以正常發起,但是返回的資料被瀏覽器截獲。
最好的例子就是CSRF跨站攻擊,有些瀏覽器不允許從HTTPS的網站跨域訪問HTTP, 比如Chorme 和 Firefox,這些瀏覽器在請求還未發出時就會攔截請求。
CORS 機制允許Web 伺服器對跨域訪問進控制,從而保證跨域資料傳輸安全進行。目前瀏覽器都支援該機制,不過需要跨域訪問需要客戶端和服務端同時支援。
服務端支援CORS
瀏覽器會針對XHR和Fetch中發起的跨域請求傳送特殊的HTTP header 請求頭資訊。並且服務端也必須返回允許使用特定的跨域響應的header 頭資訊。
跨域訪問常見場景
- XHR 或者 Fetch 發起的跨域HTTP請求
- Web 字型(通過CSS 中的@font-face使用跨域字型)
- 使用
drawImage
將image/video
畫面繪製到canvas - script
跨域資源共享(CROS)
CORS標準新增了一組HTTP首部欄位,允許伺服器宣告哪些請求源有許可權訪問哪些資源。另外,規範要求,那些可能導致伺服器產生隱患的HTTP請求方法(除GET之外的請求),瀏覽器必須首先使用OPTIONS
方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務端返回允許響應後,才發起實際的HTTP請求。在預檢請求的返回中,服務端也可以通知客戶端,是否需要攜帶身份憑證(cookies和HTTP認證相關資料)。
跨域共享實踐
這裡,我們通過XHR物件介紹三個跨域資源共享的例項。
1. 簡單請求
簡單請求的特徵:
- 不會觸發預檢請求
- 使用以下方法之一
- GET
- HEAD
- POST
Fetch 規範定義,不得自定義下面header之外的其他首部欄位
- Accept
- Accept-Language
- Content-Language
- Conetent-Type
- Viewport-width
Content-Type的值為下列之一
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
請求中任意
XMLHttpRequestUpload
物件均沒有註冊任何事件監聽器- 請求中沒有使用
ReadableStream
物件
客戶端和服務端之間使用CORS首部欄位處理跨域許可權:
檢視一個跨域請求例項:
服務端返回的 Access-Control-Allow-Origin: *
表明該資源可以被任意域名訪問,不收跨域限制。
2.預檢請求
預檢請求要求必須首先使用OPTIONS
方法發起一個預檢請求到服務端,來獲知服務端是否允許該實際請求。
預檢請求的滿足條件:
- 使用以下任意一種HTTP方法
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
- 人為設定以下集合之外的首部欄位。該集合為:
- Accept
- Accept-Language
- Content-Language
- Conetent-Type
- Viewport-width
- Content-Type 的值不屬於以下之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 請求中任意
XMLHttpRequestUpload
物件註冊多個事件監聽器 - 請求中使用了
ReadableStream
物件
OPTIONS 請求
POST 請求
執行過程:
瀏覽器首先檢查到,JS發起的請求需要被預檢。Request Method:OPTIONS
表示這個是一個預檢請求,該方法不會對伺服器資源產生影響,預檢請求中同時攜帶了下面兩個首部欄位:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
首部欄位Access-Control-Request-Method
通知服務端,實際請求將使用POST
方法。
首部欄位Access-Control-Request-Headers
通知服務端,實際請求將攜帶Content-Type
欄位。
服務端根據預檢頭資訊,判斷實際請求是否被允許。
附帶憑證的請求
通常情況下,瀏覽器傳送跨域請求時不會發送身份憑證資訊,如果要傳送憑證資訊的話,就要在對XHR物件設定特殊的標誌位。
本例中,我們對XHR物件的withCredentials
標置設定為true,從而可以向跨域的伺服器傳送Cookies資訊。
附帶身份憑證的請求不得設定萬用字元
對於帶憑證的請求,服務端不可將Access-Control-Allow-Origin
值設定為“*”。
這是因為請求的首部中攜帶Cookie資訊,如果設定為“*”,請求將會失敗。將Access-Control-Allow-Origin
值設定為請求方的host,則請求執行成功。
HTTP跨域請求首部欄位
1.Origin
Origin
表明了首部欄位預檢請求或實際請求的源端。
Origin: <origin>
2. Access-Control-Request-Method
Access-Control-Request-Method
首部欄位用於預檢請求。作用是通知實際請求使用的HTTP方法。
Access-Control-Request-Method:<method>
3.Access-Control-Request-Headers
Access-Control-Request-Headers
收不自動用於預檢請求。做使用者通知實際請求中攜帶的首部欄位通知給服務端。
Access-Control-Request-Headers:<field-name>
HTTP跨域響應首部欄位
1.Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
orgin引數允許指定外域URI訪問指定資源。對於不需要攜帶身份憑證的請求,服務端可以指定該字元為萬用字元,它允許來自所有域的訪問。
如果,允許接收指定一個具體的域請求時
Access-Control-Allow-Origin: http://google.com
服務端指定的時一個具體的域而不是“*”,那麼響應的首部中Vary
欄位值必須包含 Origin 。這將告訴客戶端:伺服器對不同源站返回不同的內容。
2.Access-Control-Expose-Headers
服務端將瀏覽器允許訪問的頭放入白名單,例如:
Access-Control-Expose-Headers: x-header
這樣,瀏覽器就可以根據服務端的響應頭白名單進行訪問。
3.Access-Control-Max-Age
指明該預檢請求可以被快取多久。
4.Access-Control-Allow-Credentials
當瀏覽器的credentials
設定為true時,是否允許瀏覽器讀取response內容。
Access-Control-Allow-Credentials: true
5.Access-Control-Allow-Methods
指明瀏覽器實際允許使用的HTTP方法。
Access-Control-Allow-Methods: <methods>
6. Access-Control-Allow-Headers
指明瀏覽器實際允許攜帶的首部欄位。
Access-Control-Allow-Headers: <filed-name>