瀏覽器的同源策略和跨域詳解(內含故事解析)
前言
去年這個時候有寫過一篇文章叫《ajax中的json和jsonp詳解》,寫這個文章是因為我朋友學習前端剛好遇到了這個問題,但是就在昨天,他在學習java的時候又遇到同樣的問題,看來我又要操作一波了。(實則我就他這一個朋友)(๑→ܫ←)
提綱內容
- 重述一遍何為同源策略(因為之前講過)
- 跨域的三種方式
- 剖析CORS方式跨域(重點)
- 故事解析(次重點)
何為同源策略
同源策略就是協議相同、域名相同、埠相同相同的網頁才能資源互通。
簡單的來說,分為三個,我訪問淘寶網站,再訪問京東網站,這兩者資源肯定不能互通是吧。
1、也就是cookie和LocalStorage、IndexDB等無法互通。
2、DOM無法獲取
也就是說幾乎所有的請求都會跨域,why?主要還不是因為很多公司為了解決web訪問對後臺造成壓力,都會把web伺服器和後臺介面伺服器分到不同的域中,前後端不分離除外,另一個原因是前端開發人員呼叫後臺測試,測試伺服器在後臺,web卻執行在自己電腦上。一個是localhost,一個是對應測試伺服器的域名。
跨域的三種方式
- JSONP方式
- CORS方式
- 代理請求方式
剖析CORS方式跨域
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)
CORS可以理解為後端配置一些東西,主動同意哪些不同域名可以請求我的介面,從而實現跨域請求的問題,(對了,提示一下,jsonp雖然簡單好用,但是隻能實現get請求,所以比較單一。但是jsonp可以適用於所有瀏覽器,CORS對ie只適用於ie10及以上,對其他瀏覽器都是支援的。)
先上一張圖(後面你會回來看的)
瀏覽器將cors請求分為兩類,簡單請求和非簡單請求,阮一峰在跨域資源共享 CORS 詳解一文中作了很詳細的區分,滿足一下兩大條件就是簡單請求,否則就是非簡單請求。如下:
1、請求方法是以下三種方法之一: * HEAD * GET * POST 2、HTTP的頭資訊不超出以下幾種欄位: * Accept * Accept-Language * Content-Language * Last-Event-ID * Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
簡單請求按鈕(我朋友寫的就是簡單請求,但是伺服器並沒有做cors配置),請求如下:
如第一張圖的執行流程,會在請求頭增加Origin欄位為"http ://localhost"到後端,後端去檢測Origin資訊,此時檢查的請求頭部有沒有自定義的,那麼問題來了。
1、為毛要再次檢查請求頭?因為你前端資訊為不可信資訊,別人可以偽造瀏覽器請求加入Origin資訊,並且自定義請求頭。
2、自定義請求頭有哪些定義有哪些?
- Accept:用來告知(伺服器)客戶端可以處理的內容型別
- Accept-Language:允許客戶端宣告它可以理解的自然語言,以及優先選擇的區域方言
- Content-Language:是一個 entity header (實體訊息首部),用來說明訪問者希望採用的語言或語言組合,這樣的話使用者就可以根據自己偏好的語言來定製不同的內容
- Content-Type實體頭部用於指示資源的MIME型別 media type(不屬於簡單請求的三個的任何一個)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
他調這個介面的時候並沒有自定義頭部,只有設定了Content-Type為application/x-www-form-urlencoded,但是這並不影響,所以現在到了判斷是否符合要求的位置,什麼符合要求呢?但是當然是跨源資源共享驗證,但是他後臺並沒有配置任何的跨域訪問,所以所有的跨域請求多會被拒絕門外,但是資訊是正常返回的,只不過返回頭裡面沒有加Access-Control-Allow-Origin這個欄位,如圖一所示。
這是時候會發生什麼情況呢?截兩張圖就知道了。
what?
瀏覽器確實收到訊息,但是他就是不給你,並且告訴你,你小子跨域了,並且伺服器不同意你的跨域請求,所以就直接拋一個錯誤給你,你自己看著辦吧!
非簡單請求步驟就多了一步
在不滿足簡單請求的情況之下(就是非簡單請求),此時,如圖一所示,並不直接傳送請求,而是傳送一個OPTIONS預檢請求(所以下次看到OPTIONS請求的時候就知道這其實是一個前菜,好戲在後頭呢。嘻嘻),後臺也是去做一個跨源資源共享驗證,他這裡沒有配置,當然不成功,所以還是正常返回,這次返回頭裡不僅不給返回Access-Control-Allow-Origin,而且不會有任何響應主體(如果你不配置的話),這也就是為啥後來他胡亂配置一通後發現post請求竟然變成OPTIONS請求了,然後不傳資料還不給返回資料的原因。請求檢視如下:
(細心了小夥伴會發現多了兩個欄位,一看就明白,不用解釋了。嘻嘻)
怕他看不懂,所以這裡也用一個小故事稍微解析一下子
首先來建立三個角色,小明(請求程式碼,也可以說是開發者)、課代表(瀏覽器)、老師(伺服器)
簡單請求:
小明(請求程式碼)做了一份試卷(請求包)交給課代表(瀏覽器),課代表分析得出,這是一份簡單試卷(簡單請求),然後寫上這是這是一班的試卷(加上Origin欄位),並且告訴老師這是一份簡單試卷,老師(伺服器)拿到試卷後,就開始閱讀。
首先看這個題(請求頭部),是不是簡單的試卷,畢竟課代表說的話不可信。
如果不簡單就直接丟了,如果是簡單試卷就再看看課代表寫的是幾班的試卷。
老師說我只改一班和三班的試卷(跨源資源共享驗證),剛好這是一班的試卷,所以就改完直接還給課代表了,課代表發現沒有異樣就直接給小明瞭。
如果這不巧是二班的試卷,老師還是把試卷給改出來(伺服器返回正常的結果),但是同時在試卷上加上一句話,“我是一班和三班的老師,以後不要發二班的試卷給我了(返回頭中沒有Access-Control-Allow-Origin欄位)”。
課代表大發雷霆,不僅不把試卷(伺服器正常結果)給小明,還警告小明(丟擲錯誤)。非簡答請求:
小明(請求程式碼)做了一份試卷(請求包)交給課代表(瀏覽器),課代表分析得出,這是一份很難的試卷(非簡單請求),然後課代表寫一封信(OPTIONS請求,裡面寫上這是一班的試卷)給老師。
老師收到這封信,老師改一班和三班的試卷,所以就批改這封信(後臺設定返回的資訊,不設定是沒有返回主體的),ok,沒問題,請把試卷給我(返回頭中有Access-Control-Allow-Origin欄位)。
然後課代表發現信沒有異樣,後面的流程就像批改簡單試卷流程一樣了。
如果,課代表傳送的信裡寫這的是“這是二班的試卷”,那麼,老師還是會批改這封信,然後正常返回這封信。關鍵是老師又加上那句話“我是一班和三班的老師,以後不要發二班的試卷給我了(返回頭中沒有Access-Control-Allow-Origin欄位)”,所以,試卷根本就沒有發給老師。注意
簡單請求是隻有一次請求的,非簡單請求是兩次請求。然後一切都透徹了啊!