CORS:跨域資源共享

定義

是由W3C提出的一個用於瀏覽器以XMLHttpRequest方式向其餘源的服務器發起請求的規範。不一樣於JSONP,CORS是以Ajax方式進行跨域請求,須要服務端與客戶端的同時支持。目前CORS在絕大部分現代瀏覽器中都是支持的(IE瀏覽器不能低於10)html

瀏覽器兼容

原理

CORS標準定義了一個規範的HTTPHeaders來使得瀏覽器與服務端之間能夠進行協商來肯定某個資源是否能夠由其餘域的客戶端請求得到。儘管不少的驗證與鑑權是由服務端完成,可是本質上大部分的檢查和限制仍是應該由瀏覽器完成。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。所以,實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。通常來講CORS會分爲簡單請求與預檢請求兩大類。api

預檢請求

當瀏覽器的請求方式知足如下的任意一個條件的時候,瀏覽器會先發送一個OPTION請求,用來與目標域名服務器協商決定是否能夠發送實際的跨域請求。OPTIONS請求頭部中會包含如下頭部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。
服務器收到OPTIONS請求後,設置Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers頭部與瀏覽器溝通來判斷是否容許這個請求。跨域

請求方法不是下列之一:瀏覽器

  • GET
  • HEAD
  • POST

請求頭中的Content-Type請求頭的值不是下列之一:緩存

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

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

請求兩次

第一次的OPTIONS請求

"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。請求頭信息裏面,關鍵字段是Origin,表示請求來自哪一個源。
除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段:cookie

  • Access-Control-Request-Method:該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是GET。
  • Access-Control-Request-Headers:該字段指定瀏覽器CORS請求會額外發送的頭信息字段.

預檢請求的返回:app

  • Access-Control-Allow-Methods:必需,它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。注意,返回的是全部支持的方法,而不單是瀏覽器請求的那個方法。這是爲了不屢次預檢請求。
  • Access-Control-Allow-Headers:若是瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在預檢中請求的字段。
  • Access-Control-Max-Age:該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是5個小時(18000秒),即容許緩存該條迴應18000秒(即5小時),在此期間,不用發出另外一條預檢請求。

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

簡單請求

當瀏覽器的請求方式是HEAD、GET或者POST,而且HTTP的頭信息中不會超出如下字段:url

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

瀏覽器會將該請求定義爲簡單請求,對於簡單的跨域請求或者經過了預檢的請求,瀏覽器會自動在請求的頭信息加上Origin字段,表示本次請求來自哪一個源(協議 + 域名 + 端口),即代表這是一個跨域請求。服務端會獲取到這個值,根據相應的跨域規則而後判斷是否贊成此次請求並返回。典型的請求頭尾:

// 請求

 - GET /cors HTTP/1.1
 - Origin: http://localhost:8080
 - Host: api.alice.com
 - Accept-Language: en-US
 - Connection: keep-alive
 - User-Agent: Mozilla/5.0...

若是服務端容許,在返回的頭信息中會多出幾個字段:

// 返回

 Access-Control-Allow-Origin: http://localhost:8080
 Access-Control-Allow-Credentials: true
 Access-Control-Expose-Headers: Info
 Content-Type: text/html; charset=utf-8
  • Access-Control-Allow-Origin:必須。它的值是請求時Origin字段的值或者 *,表示接受任意域名的請求。
  • Access-Control-Allow-Credentials:可選。它的值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器。
    再須要發送cookie的時候還須要注意要在AJAX請求中打開withCredentials屬性:withCredentials = true;

    須要注意的是,若是要發送Cookie,Access-Control-Allow-Origin就不能設爲*,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie纔會上傳,其餘域名的Cookie並不會上傳,且原網頁代碼中的document.cookie也沒法讀取服務器域名下的Cookie。

  • Access-Control-Expose-Headers:可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方
    法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-
    Modified、Pragma。若是想拿到其餘字段,就必須在Access-Control-Expose-Headers裏面指定。上面的例子指定,getResponseHeader('Info')能夠返回Info字段的值。

    若是服務端拒絕了調用,即不會帶上 Access-Control-Allow-Origin 字段,瀏覽器發現這個跨域請求的返回頭信息沒有該字段,就會拋出一個錯誤,會被 XMLHttpRequest 的 onerror 回調捕獲到。這種錯誤沒法經過 HTTP 狀態碼判斷,由於迴應的狀態碼有多是200。

總結

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

  1. 首先會檢測http請求頭是否有origin字段;
  2. 若是沒有,或者不容許,直接當成普通請求處理,結束;
  3. 若是有而且是容許的,那麼再看是不是預檢請求
  4. 若是不是預檢請求,就返回Allow-Origin、Allow-Credentials等,並返回正常內容。
  5. 若是是預檢請求,就返回Allow-Headers、Allow-Methods等,內容爲空。
相關文章
相關標籤/搜索