天天看點

同源政策與CORS

同源政策

同源政策是浏覽器保護使用者安全上網的重要措施,協定、域名、端口号三者相同即為同源。

不同源下,浏覽器不允許js操作Cookie、LocalStorage、DOM等資料或頁面元素,也不允許發送ajax請求,同源下則不受影響。

下圖是在Chrom控制台中發送ajax跨域請求的報錯資訊:

同源政策與CORS

圖檔中黃色部分提示響應被阻止,說明在跨域的情況下,請求依然發送到了伺服器且伺服器傳回了資料,隻是被浏覽器攔下了。

對于跨域問題可以使用CORS來解決,使用CORS時,HTTP請求分為兩種情況:簡單請求與複雜請求。

簡單請求

滿足以下三點即為簡單請求:

  • HTTP請求方法為GET、POST或HEAD
  • HTTP請求頭隻能包含

    Accept, Accept-Language, Content-Language, Content-Type

    Last-Event-ID

  • ContentType的值隻能為以下三種:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

複雜請求

除簡單請求之外即為複雜請求。浏覽器在發送複雜請求前會先發送Preflight request(預檢請求),即發送OPTIONS請求。注意是浏覽器發送的,使用者無感。

同源政策與CORS

預檢請求頭包含兩個特定字段:

  • Access-Control-Request-Method

    表示後續請求會用到的HTTP方法,該字段必選

  • Access-Control-Request-Headers

    後續請求中所設定的請求頭部資訊,注意,這裡不包含浏覽器預設設定的頭部字段,如:

    User-Agent

    。該字段可不發送。

伺服器會檢查對預檢請求中的

Origin

Access-Control-Request-Method

Access-Control-Request-Headers

字段值,并傳回正常的HTTP響應。

浏覽器根據傳回資訊判斷後續請求是否符合伺服器端跨域要求,不符合則抛出錯誤資訊。通過預檢請求後,則發送後續請求,此時和簡單請求無差别。

伺服器配置CORS的幾個字段

  • Access-Control-Allow-Origin

    必選,設定允許哪些源通路伺服器資源

  • Access-Control-Allow-Methods

    必選,設定允許哪些HTTP方法

  • 設定HTTP請求頭中包含哪些字段,如果浏覽器請求包括

    Access-Control-Request-Headers

    字段,則必選

以上三個字段為常用字段,其餘字段配置參考:CORS policy options。

withCredentials與Cookie的跨域問題

Cookie受到同源政策的限制沒有那麼嚴格,預設情況下,隻要發送請求方所在域與Cookie的Domain值相同即可将cookie發送至伺服器端,無需考慮協定和端口号。在預設情況下,用戶端發起的HTTP請求會帶上目标域的Cookie,但無法攜帶其它屬于其它的域Cookie。

我們可以借助XMLHttpRequest

對象的withCredentials屬性及CORS的Access-Control-Allow-Credentials二者來實作跨域的Cookie發送和寫入。

var xhr=new XMLHttpRequest();
xhr.open('GET','http://www.target.com:8093/api/GetAllProductType');
xhr.onreadystatechange=function(){
    if(xhr.readyState==4 && xhr.status==200){
        console.log(xhr.response);
    }
}
// 這裡使用withCredentials屬性來發送Cookie
xhr.withCredentials=true;
xhr.send();      

注意,在使用

withCredentials

時,伺服器端不能将

Access-Control-Allow-Origin

的值配置為*,否則用戶端會報錯:

Access to XMLHttpRequest at ''http://www.target.com:8093/api/GetAllProductType' from origin 'http://www.request.com:8094' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

一個問題

上周在ASP.NET Web API 2中使用CORS,報錯:

The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed

。原因是伺服器端配置了兩次CORS,導緻傳回了兩個

Access-Control-Allow-Origin:*

但浏覽器隻允許一個。

經過排查發現在

Web.config

檔案中也配置了CORS,與代碼中的配置重複,注釋掉之後問題解決。該問題參考了:stackoverflow上的回答。

小結

同源政策是浏覽器為保障使用者(資料)安全而對JS功能進行一定限制。畢竟HTML與CSS隻負責網頁結構與樣式,不具備操作頁面元素及與伺服器互動的功能。

離開浏覽器環境後跨域問題也就不複存在。

嚴格的限制會導緻一些不便,故同源政策開了幾個口子:

  • Cookie共享

    子域名可以共享父級域名的cookie

  • 嵌入式資源擷取

    <script>,<img>,<link>

    等标簽擷取資源不受同源政策限制,這也是JSONP實作跨域的原理

常用處理跨域請求的方式有JSONP和CORS:

  • JSONP

    需要前後端協作處理且隻支援GET請求

    不是标準規範

    對老式浏覽器友好(這裡想到了老古董IE:)

  • CORS

    支援GET、POST、PUT、DELETE等多種請求

    伺服器端配置簡單且不需要前端寫額外的代碼

    目前主流浏覽器均支援CORS規範

推薦閱讀

浏覽器家族的安全反擊戰

Enable Cross-Origin Requests (CORS) in ASP.NET Core

前後端分離 | 關于登入狀态那些事

Cross-Origin Resource Sharing (CORS)

Cookie中的幾個概念

當 CORS 遇到 SameSite

跨站(cross-site)、跨域(cross-origin)、SameSite與XMLHttpRequest.withCredentials