原文地址: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大致流程圖
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會報錯,提示權限不允許,如下圖:
可以總結簡單請求流程如下
複雜請求
如果僅僅是簡單請求,那麼即便不用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。
實際迴應如下圖
在這裏實際迴應與簡單請求一樣。
總結複雜請求過程
總結
到目前爲止,跨域請求到目前爲止理論基礎就完了。
今天出現的問題是ajax請求後端接口的時候,無法帶上cookie,剛開始以爲是cookie下發的domain問題;修改後,發現依然無法帶上cookie,後來才發現,原來是跨域請求了。跨域請求默認不帶cookie。當js設置withCredentials爲true,但是Console裏會報錯,然後查看服務器設置,服務器Access-Control-Allow-Credentials設置爲false,修改服務器設置後,問題才完整解決了。
歡迎添加公衆號: