1. 程式人生 > >HTTP(二)、跨域資源共享(CORS)

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使用跨域字型)
  • 使用drawImageimage/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>