跨域請求原理及分析

原文地址:http://blog.onlywan.cc/15005393189989.html

整理原因

因爲做服務端開發,一直對跨域請求只是瞭解的地步,今天剛好前端有一個問題,找到我,確定問題的時候,走了一些彎路,最後確認問題出在跨域請求上,所以感覺自己對跨域只是瞭解,而沒有進行過整理,所以這裏對跨域請求的一些知識進行一些整理

什麼算作跨域

跨域,指瀏覽器不能執行其他網站的腳本,他是由瀏覽器同源策略造成的,是瀏覽器對JavaScript施加的安全限制

所謂同源是指,域名,協議,端口均相同,舉個例子(”源”請求”目標”)

目標 說明
http://www.a.com/a.html http://www.a.com/b.php 非跨域
http://www.a.com/a.html http://www.a.com/api/b.php 非跨域
http://www.a.com/a.html http://www.b.com/b.php 跨域,主域名不同:a/b
http://www.a.com/a.html http://api.a.com/b.php 跨域,子域名不同:www/api
http://www.a.com/a.html http://www.a.com:8080/b.php 跨域,端口號不同:80/8080
http://www.a.com/a.html https://www.a.com/b.php 跨域,協議不同:http/https

跨域解決方法

網上解決方案說法很多,沒有一一驗證,我比較熟悉的兩種方法

JSONP

原理:JSONP利用了\

    1. 只能GET請求;
    2. 因爲使用\<script\>標籤加載JS腳本,所以受瀏覽器加載JS源線程數量限制
    3. 因爲JSONP屬於腳本注入行爲,存在安全隱患
    4. 需要服務端配合,導致服務端接口無法做成通用接口
    ...(還有其他缺點,未驗證)

CORS

原理:CORS(Cross-Origin Resource Sharing)定義了在跨域訪問資源時瀏覽器和服務器之間如何通信。簡單點說就是瀏覽器與服務器之間的一種協議,使用http頭部信息讓瀏覽器與服務器互相瞭解,從而決定請求與響應成功與否(需瀏覽器支持,也基本都支持~)

下面主要介紹CORS原理

CORS知識整理

CORS大致流程圖

Created with Raphaël 2.1.0JavaScriptJavaScript瀏覽器瀏覽器服務器服務器xhr.send();預先請求(如果需要)預先回應(如果需要)實際請求實際迴應觸發onload或者onerror

CORS分類

CORS可以分爲兩種:

  • 簡單請求
  • 複雜請求

一個簡單請求大致如下:

  • HTTP方法是下列之一
    * HEAD
    * GET
    * POST
  • HTTP頭包含
    * Accept
    * Accept-Language
    * Content-Language
    * Last-Event-ID
    * Content-Type (但僅能是下列之一)
        ** application/x-www-form-urlencoded
        ** multipart/form-data
        ** text/plain

任何一個不滿足上述要求的請求,即被認爲是複雜請求。一個複雜請求不僅有包含通信內容的請求,同時也包含預請求( preflight request )

下面使用tcpdump抓包驗證(tcpdump命令大家可以自己百度,我這是抓取內環網絡80端口的報文)

本地測試使用www.wan1.com下請求www.wan2.com接口,模擬跨域請求,從請求包中可以看到,Refer爲www.wan1.com/test.html,請求host爲www.wan2.com,請求接口testput

簡單請求的發送從報文上看與普通請求沒有太大區別,但是在HTTP header中包含了一個域 Origin 的信息(如第二個紅框所示)。不過這一項實際上由瀏覽器代爲發送,並不是開發者代碼可以觸及到的。

下面是HTTP迴應

在迴應中,CORS相關的項目全都是以 “Access-Control-“作爲前綴的,其意義分別如下:

  • Access-Control-Allow-Origin(必含)- 不可省略,否則請求按失敗處理。該項控制數據的可見範圍,如果希望數據對任何人都可見,可填寫“*”。
  • Access-Control-Allow-Credentials(可選)- 該項標誌着請求當中是否包含cookies信息,只有一個可選值:true(必須爲小寫)。如果不包含cookies,請略去該項,而不是填寫false。這一項與Ajax請求中withCredentials屬性應保持一致,即withCredentials爲true時該項也爲true;withCredentials爲false時,省略該項不寫。反之,則會導致請求失敗。

經測試,如果Access-Control-Allow-Origin服務器下發值爲 http://www.wan3.com,那麼ajax中是得不到服務器返回的值的,但是在Chrome中開發者模式中Network中會看到請求正常返回,並能看到header中設置的值爲http://www.wan3.com。如下圖:

請求返回值如下圖:

但是在Console會報錯,提示權限不允許,如下圖:

可以總結簡單請求流程如下

Created with Raphaël 2.1.0JavaScriptJavaScript瀏覽器瀏覽器服務器服務器簡單請求;添加Origin拒絕訪問或允許訪問並返回允許的Origin根據Origin觸發onload或者onerror

複雜請求

如果僅僅是簡單請求,那麼即便不用CORS也沒有什麼大不了,但CORS的複雜請求就令CORS顯得更加有用了。簡單來說,任何不滿足上述簡單請求要求的請求,都屬於複雜請求。比如說你需要發送PUT、DELETE等HTTP動作,或者發送Content-Type:application/json的內容。

複雜請求表面上看起來和簡單請求使用上差不多,但實際上瀏覽器發送了不止一個請求。其中最先發送的是一種“預請求”,此時作爲服務端,也需要返回“預迴應”作爲響應(即如CORS流程圖中所畫)。預請求實際上是對服務端的一種權限請求,只有當預請求成功返回,實際請求才開始執行。

使用tcpdump抓包,第一個報文如下:

預請求以OPTIONS請求發送,當中同樣包含域,並且還包含了兩項CORS特有的內容:

  • Access-Control-Request-Method - 該項內容爲實際請求的種類,可以是GET,POST之類的簡單請求,也可以是PUT,DELETE等等。
  • Access-Control-Request-Headers - 該項是一個以逗號分隔的列表,當中是複雜請求所使用的的頭部(這裏爲了製造複雜請求,添加了一個key爲wan的header值)

顯而易見,這個預請求就是在爲之後的實際請求發送一個權限請求,在預迴應返回的內容當中,服務器端應當對這兩項進行回覆,以讓瀏覽器確認請求是否能夠成功完成。例如,剛纔的預請求獲得的服務端如下回應

來看預迴應當中的項目:

  • Access-Control-Allow-Origin(必含)- 和簡單請求一樣的,必含的一個域。
  • Access-Control-Allow-Methods(必含)- 這是對預請求當中的Access-Control-Request-Method的回覆,這一回復將是一個以逗號分隔的列表。儘管客戶端或許只請求某一方法,但是服務端仍然可以返回所有方法,以便客戶端將其緩存
  • Access-Control-Allow-Headers(當預請求中包含Access-Control-Request-Headers時必須含)- 這是對預請求中Access-Control-Request-Headers的回覆,和上面一樣是以逗號分隔的列表,可以返回所有支持的頭部
  • Access-Control-Allow-Credentials(可選)- 和簡單請求中相同
  • Access-Control-Max-Age(可選)– 以秒爲單位的緩存時間。預請求的的發送並非免費午餐,允許時應當儘可能緩存。

一旦預迴應返回後,所請求的權限都已滿足後,則開始發送實際請求

如圖所示即爲負責請求中允許添加的header。

實際迴應如下圖

在這裏實際迴應與簡單請求一樣。

總結複雜請求過程

Created with Raphaël 2.1.0JavaScriptJavaScript瀏覽器瀏覽器服務器服務器xhr.send();預先請求,驗證權限預先回應,權限允許實際請求實際迴應觸發onload或者onerror

總結

到目前爲止,跨域請求到目前爲止理論基礎就完了。

今天出現的問題是ajax請求後端接口的時候,無法帶上cookie,剛開始以爲是cookie下發的domain問題;修改後,發現依然無法帶上cookie,後來才發現,原來是跨域請求了。跨域請求默認不帶cookie。當js設置withCredentials爲true,但是Console裏會報錯,然後查看服務器設置,服務器Access-Control-Allow-Credentials設置爲false,修改服務器設置後,問題才完整解決了。

歡迎添加公衆號:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章