1. 程式人生 > >關於CORS跨域問題的理解

關於CORS跨域問題的理解

當前 get 說明 multipart 服務 tom HP gin 範圍

起因

因為這段時間一個項目前後端分別部署在不同服務器的需要,抽空學習了一下CORS問題,不足之處,歡迎指教.

什麽是CORS

CORS是一個w3c標準,全稱是"跨域資源共享"(Cross-origin resource sharing),但一個請求url的協議,域名,端口三者之間任意與當前頁面地址不同即為跨域.它允許閱覽器向跨源服務器發送XMLHttpRequest請求,從而客服AJAX只能同源使用的限制.

CORS簡介

瀏覽器默認的安全限制為同源策略,即JavaScript或Cookie只能訪問同源(相同協議,相同域名,相同端口)下的內容。但由於跨域訪問資源需要,出現了CORS機制,這種機制讓web服務器能跨站訪問控制,使跨站數據傳輸更安全。CORS需要閱覽器和服務器同時支持,目前,主流的閱覽器都支持cors。

技術分享圖片

CORS的兩種請求方式

瀏覽器將CORS請求分為兩類:簡單請求和非簡單請求

一 簡單請求

1.1 區分條件:

只要滿足一下兩大條件,屬於簡單請求:

(1) 請求方法是以下三種方法之一:
        HEAD
        GET
        POST
(2)HTTP的頭信息不超出以下幾種字段:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain        

不同時滿足上面兩個條件,就是非簡單請求

1.2 簡單請求的基本流程

瀏覽器直接發送CORS跨域請求,並在header信息中增加一個Origin字段,表明這是一個跨域的請求。

    <script>
        var url = ‘http://www.lishanlei.cn/CorsTest/CorsTest.php‘;
        var xhr = new XMLHttpRequest();
        xhr.open(‘get‘, url, true);
        // xhr.setRequestHeader(‘X-Custom-Header‘, ‘value‘);
        xhr.send();
    </script>

在上面的代碼中以get形式向www.lishanlei.cn進行跨域訪問。

技術分享圖片

上面的頭信息Origin字段用來說明本次請求來自哪個源(協議+域名+端口)。服務器根據這個值,決定是否同意這次請求。如果Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP回應,瀏覽器收到這個回應發現這個回應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道錯了,從而會拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。註意這種錯誤無法通過狀態碼識別,此時HTTP回應的狀態碼可能是200

技術分享圖片

技術分享圖片

如果Origin指定的域名在許可範圍內,服務器返回的響應會多出幾個頭信息字段

技術分享圖片

Access-Control-Allow-Origin:該字段是必須的,其值可能是請求時Origin字段的值,也可能是一個*,表示接受任意域名請求。

Access-Control-Allow-Credentials:該字段可選,其值類型是布爾型,表示是否允許發送Cookie。默認情況下Cookie不包括在CORS請求中。當設為true時表示服務器明確許可,Cookie可以包含在請求中一起發送給服務器。

Access-Control-Allow-Headers:該字段必須,它是一個都好分割的字符串,表明服務器支持的所有頭信息字段

1.3 withCredentials屬性

默認情況下,CORS請求默認不發送Cookie和Http認證信息,如果要把Cookie發送到服務器,首先要指定Access-Control-Alloe-Credentials字段,另一方面,需要在AJAX請求中打開withCredentials屬性

var xml = new XMLHttpRequest();
xml.withCredentials = true;

如果沒有設置該屬性為true,即使服務器統一發送Cookie,瀏覽器也不會發送。

技術分享圖片

當該屬性設置為true時,即需要發送Cookie那麽在服務器中Access-Control-Allow-Origin就不能設置成*,必須指定嗎明確的與請求一致的域名,同時,Cookie依然遵循同源政策。

技術分享圖片

二 非簡單請求

當不同時滿足區分條件的,就是非簡單請求。

2.1 預檢請求

非簡單請求的CORS請求,會在正式通信前進行一次Http查詢請求,又稱預檢請求。

瀏覽器先請求服務器,當前網頁所在域名是否在服務器許可名單中以及可以使用那些HTTP動詞和頭信息字段,當客戶端得到肯定答復時,瀏覽器才會正式發出XMLHttpRequest請求。

var url = ‘http://www.lishanlei.cn/CorsTest/CorsTest.php‘;
var xhr = new XMLHttpRequest();
xhr.open(‘PUT‘, url, true);
xhr.setRequestHeader(‘X-Custom-Header‘, ‘value‘);
xhr.send();

上面代碼中,請求方式是PUT,並發送一個自定義頭信息X-custom-Header,但在服務器的頭信息中沒有包含X-custom-Header,故瀏覽器報錯,只進行了一次預檢請求。

技術分享圖片

技術分享圖片

設置header中的Access-Control-Allow-Headers字段:

header(‘Access-Control-Allow-Headers:x-requested-with,content-type,X-Custom-Header‘);

再次訪問

技術分享圖片

瀏覽器發現這是一個非簡單請求,自動發出一個預檢請求,要求服務其確認這樣的請求,下面是這個預檢請求的HTTP頭信息:

技術分享圖片

預檢請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。

Access-Control-Request-Method:該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法。

Access-Control-Request-Headers:該字段是一個逗號分割的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段。

2.2預檢請求的回應

當服務其收到預檢請求後,檢查了Origin,Access-Control-Request-Method等信息字段後,如果沒有問題,則確認允許跨源請求,就可以做出了回應。

技術分享圖片

Access-Control-Allow-Methods:該字段必須,其值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。這是為了避免多次預檢請求。

Access-Control-Max-Age:該字段是可選的,用來指定本次預檢請求的有效期,單位是秒。Access-Control-Max-Age:20,即允許緩存該條回應20秒,再此期間不用發出另一條預檢請求

如果瀏覽器否定了預檢請求,會返回一個正常的HTTP回應,但是沒有任何CORS相關的頭信息字段。瀏覽器此時會認定服務器不同意預檢請求,觸發一個錯誤,被XMLHttpRequestonerror回調函數捕獲。

服務器端處理機制

服務器對於跨域請求的處理流程如下:

  1. 首先查看http頭部有無origin字段;
  2. 如果沒有,或者不允許,當成普通請求;
  3. 如果有且是允許的,再看是否是preflight(method=OPTIONS);
  4. 如果不是preflight(簡單請求),返回Allow-Origin,Allow-Credential等字段,並返回正常內容;
  5. 如果是preflight(非簡單請求),返回Allow-Headers,Allow-Methods等;

配置CORS規則

一 apache上配置CORS規則

Apache需要使用mod_headers模塊來激活HTTP頭設置,默認是激活的,只需要修改Apache配置文件中的/etc/apache2/sites-available/000-default.conf

1 開啟模塊 
sudo a2enmod headers 
2 編輯配置文件 
sudo vi /etc/apache2/sites-available/000-default.conf 
3 在虛擬主機Directory設置下添加 
Header set Access-Control-Allow-Origin *

與JSONP的比較

CORS和JSONP都是為了使web瀏覽器能夠跨源請求,使用目的相同,但是比JSONP更強大。JSONP只支持GET請求,而CORS支持所有類型的HTTP請求,不過JSONP的優勢在於支持老式瀏覽器以及可以向不支持CORS的網站跨源請求。

參考博文http://www.ruanyifeng.com/blog/2016/04/cors.html

https://blog.csdn.net/u014344668/article/details/54948546

關於CORS跨域問題的理解