跨域資源共享CORS

本人是一個應屆生,面試的時候常常會被問到跨域的問題,CORS固然也是解決跨域的方法之一了。可是當面試官繼續問:「CORS跨域是怎麼實現的?爲何會有OPTIONS請求呢?OPTIONS請求有什麼用途呢?」可能回答的就不是那麼完美。html

因此,就總結概括瞭如下關於CORS的詳細知識。。。面試

參考文章:
跨域資源共享CORS詳解
爲何會有OPTIONS請求
瀏覽器同源策略及其規避方法
HTTP訪問控制(CORS)json

什麼是CORS?

CORS是W3C標準,全稱「跨域資源共享(Corss-orign resource sharing)」。後端

它容許瀏覽器向跨源服務器發送XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。api

CORS須要瀏覽器和服務器同時支持;整個CORS的通訊過程都是瀏覽器自動完成的,用戶不須要參與;CROS通訊和同源的AJAX通訊無差異;瀏覽器檢測到AJAX請求跨源時會自動添加一些附加的頭信息有時候還會多一次附加請求,可是用戶不會有感受。跨域

CORS通訊的關鍵仍是服務器,只要服務器實現了CORS的接口,就能夠跨源通訊。瀏覽器

CORS分爲兩類

CORS請求分爲兩大類:簡單請求和非簡單請求,瀏覽器對這兩種請求的處理是不同的。安全

知足如下條件就是簡單請求:bash

  • 請求方法:GETPOSTHEAD
  • HTTP頭信息不超過如下幾種:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:application/x-www-form-urlencode、mulitpart/form-data、text/plain(只限於這三個值)

簡單請求

對於簡單請求,瀏覽器會直接發出CORS請求;瀏覽器發現這次跨源的AJAX請求是簡單請求,就自動在請求頭信息中添加Origin字段來講明本次請求來自哪一個源(域名+端口+協議);服務器根據這個值來決定是否贊成此次請求。服務器

若是Origin指定的源不在許可的範圍內:
服務器會返回一個正常的HTTP響應,可是這個響應的頭信息沒有包含Access-Control-Allow-Origin字段,會拋出錯誤,被XMLHttpRequestonerror回調函數捕獲(!!這種狀況狀態碼沒法識別,返回的極可能是200)。

若是Origin指定的源在許可的範圍內:
服務器返回的響應會多幾個字段:

  • Access-Control-Allow-Origin:必須!要麼是個域名,要麼是個*,表示接受任何域名的請求。
  • Access-Control-Allow-Credentials:可選!是一個布爾值,表示容許發送cookie;默認狀況下,cookie不包含在CORS請求中。該字段設爲true時,表示服務器許可,cookie能夠包含在請求中,一塊兒發送給服務器。
  • Access-Control-Expose-Headers:可選!CORS請求時,XMLHttpRequest對象的getResponseHeader()只能拿到6個基本字段,Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。若是想拿到其餘字段就要在Access-Control-Expose-Headers中指定,getResponseHeader('FooBar'),就能夠返回FooBar的值。

withCredentials屬性

上面說到CORS請求默認不會發送cookieHTTP的認證信息,因此要把cookie發送到服務器不但要服務器贊成設置

Access-Control-Allow-Credentials:true 
複製代碼

還要開發者在AJAX請求中設置withCredentials屬性

var xhr = new XMLHttpRequest(); 
xhr.withCredentials = true;  
複製代碼

不然,即便服務器贊成發送cookie,瀏覽器也不會發送。 !!!若是要發送cookie,那麼Access-Control-Allow-Origin的值不能是*,必須是指定明確的,與請求網頁一致的域名;同時,cookie依然遵循同源策略,只有用服務器域名設置的cookie纔會上傳,其餘域名的cookie不會上傳,且(跨源)原網頁代碼中document.cookie也沒法讀取服務器域名下的cookie

非簡單請求

當知足下面任意條件時,會發送預檢請求:

  • 使用了下面的任意方法:
    • PUT
    • DELETE
    • CONNECT
    • OPTIONS
    • TRACE
    • PATCH
  • 人爲設置了對CORS安全的首部字段集合的其餘首部字段,該集合有:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(but note the addtional requirements below)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-width
    • Width
  • Content-Type的值不屬於下列之一:
    • application/x-www-form-urlencode
    • mulitpart/form-data
    • text/plain

預檢請求Preflighted Requests

非簡單請求的CORS請求,會在正式通訊以前,增長一次HTTP查詢請求(‘預檢’請求)。preflighted requestsCORS中一種透明的服務器驗證機制。預檢請求首先會向另外一個域名資源發送HTTP OPTIONS請求頭,來驗證發送的請求是否安全。

瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器許可的名單之中,以及可使用哪些HTTP動詞和頭信息字段。只有獲得確定的答覆,瀏覽器纔會發出正式的XMLHttpRequest請求,不然會報錯。

瀏覽器發現,是一個非簡單請求,就自動發送一個「預檢」請求,要求服務器確認。

預檢請求的請求方法是OPTIONS,表示詢問;頭信息裏的關鍵字是Origin,表示請求來自哪一個源除了Origin還有兩個字段:

  • Access-Control-Request-Method:列出瀏覽器的CORS請求會用到哪些HTTP方法。
  • Access-Control-Request-Headers:指定瀏覽器CORS請求會額外發送的頭信息字段。

OPTIONS請求的用途?

一、獲取服務器支持的HTTP請求方法。
二、用來檢查服務器的性能。

AJAX進行跨域請求時的預檢,須要向另外一個域名的資源發送一個HTTP OPTIONS的請求頭,
用以判斷實際的請求是否安全。(這個是瀏覽器加上的,後端沒有作任何操做)
複製代碼

爲何會有OPTIONS請求?

規範要求,對那些可能對服務器數據產生反作用的HTTP請求方法(特別是GET之外的HTTP請求,或搭配某些MIME類型的POST請求),瀏覽器必須首先使用OPTIONS發送一個預檢請求,來獲取服務器是否容許該跨域請求。服務器確認容許跨域後,瀏覽器再發送實際的HTTP請求。

爲何沒有發生預檢請求?
在跨域請求服務器時,設置了Content-Typeapplication/json,後,按理說,應該是會發起預檢請求。可實際結果與理論發生了衝突。

緣由在於:預檢請求須要在服務器中進行配置,在修改該路由的代碼爲如下內容後,瀏覽器正確地發起了OPTIONS預檢請求。

const cors=require('cors');
router.options('/api/test/corsopt',cors());
router.post('/api/test/corsopt',cors(),(req,res)=>{
 res.end('test');
})
複製代碼

預檢請求的迴應

服務器接受了預檢請求之後,會檢查OriginAccess-Control-Request-MethodsAccess-Control-Request-Headers字段之後,確認容許跨域請求,就會作出迴應。
若是瀏覽器否認了預檢請求,會返回一個正常的HTTP響應,可是沒有任何CORS相關的頭信息字段。這時瀏覽器就會認爲服務器不一樣意預檢請求,所以觸發一個錯誤,被XMLHttpRequest對象的onerror函數捕獲。該錯誤信息爲:

XMLHttpRequest cannot load http://xxx.xxx.com.  
Origin http://xxx.xxx.com is not allowed by Access-Control-Allow-Origin.
複製代碼

服務器響應的其餘CORS相關字段:

  • Access-Control-Request-Methods:列出瀏覽器的CORS請求會用到哪些HTTP方法(返回的是全部支持的方法)
  • Access-Control-Request-Headers:表示服務器支持的全部頭信息字段,不限於瀏覽器在「預檢」請求的字段。
  • Access-Control-Allow-Credential:與簡單請求的意義相同。
  • Access-Control-Max-Age:可選!本次預檢請求的有限期,該期限期間不會再發送預檢請求。

瀏覽器的正常請求和迴應

一旦服務器經過了「預檢」請求,之後每次瀏覽器正常的CORS請求,都和簡單請求同樣,會有一個Origin頭信息字段;服務器響應也是同樣會有一個Access-Control-Allow-Origin頭信息字段。

相關文章
相關標籤/搜索