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

  • 在前后端對(duì)接口的時(shí)候,有時(shí)瀏覽器控制臺(tái)會(huì)出現(xiàn)一諸如此類的報(bào)錯(cuò):

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.
這段話的大概意思呢,就是不允許跨域請(qǐng)求資源

  • 狀態(tài)碼

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

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

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

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

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

1. 使用CORS時(shí),異步請(qǐng)求會(huì)被分為簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求

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

a. 簡(jiǎn)單請(qǐng)求

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

 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: 請(qǐng)求字段為時(shí),表示接受任意域名的請(qǐng)求(如果需要cookie時(shí),不能設(shè)置為
Access-Control-Allow-Credentials: 該字段可選,是一個(gè)布爾值,表示是否發(fā)送Cookie,默認(rèn)為false
Access-Control-Expose-Headers: 該字段可選,可選值有:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma

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

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的請(qǐng)求方式是PUT,并且發(fā)送一個(gè)自定義頭信息X-Custom-Header。
瀏覽器發(fā)現(xiàn)這是一次非簡(jiǎn)單請(qǐng)求,就自動(dòng)發(fā)出“預(yù)檢”請(qǐng)求,要求服務(wù)器確認(rèn)可以這樣請(qǐng)求,"預(yù)檢"請(qǐng)求的頭信息:

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...

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

"預(yù)檢"請(qǐng)求的回應(yīng)(檢查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: 返回所有支持的方法:避免多次"預(yù)檢"請(qǐng)求;
Access-Control-Allow-Headers: 如果瀏覽器有Access-Control-Request-Headers請(qǐng)求,則為必須;
Access-Control-Allow-Credentials: 該字段可選,是一個(gè)布爾值,表示是否發(fā)送Cookie,默認(rèn)為false;
Access-Control-Max-Age: 可選,指定本次預(yù)檢請(qǐng)求的有效期;

瀏覽器的正常請(qǐng)求和回應(yīng)
當(dāng)服務(wù)器通過"預(yù)檢"請(qǐng)求之后,以后每次瀏覽器CORS請(qǐng)求都和簡(jiǎn)單請(qǐng)求一樣,"預(yù)檢"請(qǐng)求之后瀏覽器的請(qǐng)求:

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...

服務(wù)器響應(yīng):

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

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

局限性

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容