HTTP跨域的原因及解決方法(CORS 和 JSONP)

  • 在前后端對接口的時候,有時瀏覽器控制臺會出現(xiàn)一諸如此類的報錯:

XMLHttpRequest cannot load http://xxxxx. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://xxxxx' is therefore not allowed access. The response had HTTP status code 405.
這段話的大概意思呢,就是不允許跨域請求資源

  • 狀態(tài)碼

  1. 狀態(tài)碼分類
    1** - 信息,服務器接收到請求,需要請求者繼續(xù)執(zhí)行操作
    2** - 成功,操作被成功接受并處理
    3** - 重定向,需要進一步的操作以完成請求
    4** - 客戶端錯誤,請求包含語法錯誤或無法完成請求
    5** - 服務端錯誤,服務器在處理請求的過程中發(fā)生了錯誤
  2. 常見狀態(tài)碼
    200 - 請求成功
    301 - 資源(網頁等)被永久轉移到其他URL
    404 - 請求的資源(網頁等)不存在
    405 - 客戶端請求中的方法被禁止
    500 - 內部服務器錯誤
  • 為什么會出現(xiàn)跨域問題?

  1. 什么是跨域請求
    瀏覽器同源策略的限制(訪問同源的資源是被瀏覽器允許的,但是如果訪問不同源的資源,瀏覽器默認不允許。訪問不同源的資源就叫做跨域)
  2. 什么是同源策略(Same Origin Policy)?
    同源策略,是瀏覽器的一種核心最基本的安全策略。它對來之不同遠的文檔或腳本對當前文檔的讀寫操作做了限制。同源,即協(xié)議相同,域名相同,端口相同
  3. 為什么會有跨域問題
    跨域問題只出現(xiàn)在瀏覽器訪問的頁面,因為這是瀏覽器為了保戶用戶安全而制造的策略。假如沒有這層保護,網站就很容易受到跨站偽造請求(CSRF)的攻擊。

瀏覽器的兩種同源策略會造成跨域:

  • DOM同源策略:禁止對不同源的頁面的DOM進行操作,主要包括iframe、canvas之類的。不同源的iframe禁止數(shù)據交互的,含有不同源數(shù)據的canvas會受到污染而無法進行操作。
  • XmlHttpRequest同源策略:禁止不同源的AJAX請求,主要用來防止CSRF攻擊
  • 怎么解決跨域限制

  1. CORS(跨資源共享- Cross-origin resource sharing)
    CORS 是W3C推薦的一種官方方案,能使服務器支持XmlHttpRequest的跨域請求。CORS只需要添加一些HTTP頭,讓服務器聲明允許的訪問來源。

1. 使用CORS時,異步請求會被分為簡單請求和非簡單請求

  • 簡單請求需要滿足以下兩大條件:
  1. 請求方法是以下三種之一:
  • HEAD
  • GET
  • POST
  1. HTTP 的頭信息不超出以下幾種字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-type:只限于3個值application/x-www-from-urlencoded、multipart/from-data、text/plain
  • 凡是不同時滿足以上兩個條件的,均屬于非簡單請求

a. 簡單請求

對于簡單請求,瀏覽器直接發(fā)出CORS請求。在頭信息中自動添加一個Origin字段(本次請求來自哪個源:協(xié)議+域名+端口),服務器根據這個值,決定是否同意這次請求。
如果Origin指定的源,不在許可范圍內,服務器返回一個正常的HTTP回應。頭信息中沒有包含Access-Control-Allow-Origin字段,便會拋出錯誤:被XMLHttpRequest的onerror回調函數(shù)捕獲。
如果Origin指定的源在許可范圍內,服務器會返回的響應會多出:

 Access-Control-Allow-Origin: http://api.XXX.com
 Access-Control-Allow-Credentials: true
 Access-Control-Expose-Headers: FooBar
 Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin: 請求字段為時,表示接受任意域名的請求(如果需要cookie時,不能設置為
Access-Control-Allow-Credentials: 該字段可選,是一個布爾值,表示是否發(fā)送Cookie,默認為false
Access-Control-Expose-Headers: 該字段可選,可選值有:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma

b. 非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
非簡單請求之前,會增加一次HTTP查詢請求,稱為“預檢”請求。
瀏覽器先詢問服務器,當前網頁所在域名是否在服務器許可名單中,以及可以使用哪些HTTP動詞和頭信息字段,只有得到肯定的答復,瀏覽器才會發(fā)出XMLHttpRequest請求。

var url = 'http://api.aaa.com/a';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面這段代碼中,HTTP的請求方式是PUT,并且發(fā)送一個自定義頭信息X-Custom-Header。
瀏覽器發(fā)現(xiàn)這是一次非簡單請求,就自動發(fā)出“預檢”請求,要求服務器確認可以這樣請求,"預檢"請求的頭信息:

OPTIONS /cors HTTP/1.1
Origin: http://api.bbb.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.aaa.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

"預檢"請求方法是OPTIONS,表示這個請求是用來詢問的,頭信息里邊的關鍵字Origin表示來源。
Access-Control-Request-Method: 該字段是必須的,用來列出瀏覽器的CORS會用到哪些HTTP方法。
Access-Control-Request-Headers: 指定瀏覽器CORS請求會額外發(fā)送的頭信息字段,如’token‘,'equipment'

"預檢"請求的回應(檢查Origin、Access-Control-Request-Method、Access-Control-Request-Headers后)

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

Access-Control-Allow-Methods: 返回所有支持的方法:避免多次"預檢"請求;
Access-Control-Allow-Headers: 如果瀏覽器有Access-Control-Request-Headers請求,則為必須;
Access-Control-Allow-Credentials: 該字段可選,是一個布爾值,表示是否發(fā)送Cookie,默認為false;
Access-Control-Max-Age: 可選,指定本次預檢請求的有效期;

瀏覽器的正常請求和回應
當服務器通過"預檢"請求之后,以后每次瀏覽器CORS請求都和簡單請求一樣,"預檢"請求之后瀏覽器的請求:

PUT /a HTTP/1.1
Origin: http://api.bbb.com
Host: api.aaa.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服務器響應:

Access-Control-Allow-Origin: http://api.bbb.com
Content-Type: text/html; charset=utf-8

2. JSONP
可以在js中繞過同源策略,并發(fā)起跨域HTTP請求的使用模式。
同源策略有一個顯著的例外,HTML腳本元素可以規(guī)避SOP檢查,所以我們可以通過script標簽動態(tài)注入腳本向其他源發(fā)送請求。

局限性

  1. JSONP只適用于http的get請求。
  2. JSONP缺乏錯誤處理機制,如果動態(tài)注入腳本成功,就會執(zhí)行回調函數(shù),如果錯誤,沒有任何提示信息;即當遇到4,5等錯誤的時候,沒法找到錯誤的原因;
  3. 在安全方面,借助JSONP有可能進行跨站請求偽造(CSRF)攻擊,當一個惡意網站使用訪問者的瀏覽器向服務器發(fā)送請求并進行數(shù)據變更時,被稱為CSRF攻擊。由于請求會攜帶cookie信息,服務器會認為是用戶自己想要提交表單或者發(fā)送請求,而得到用戶的一些隱私數(shù)據
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容