跨域限制是瀏覽器行爲,不是服務器行爲。
瀏覽器有個同源策略,對於不同源的站點之間的相互請求會做限制。如果非同源,共有三種行爲受到限制:
- Cookie、LocalStorage 和 IndexDB 無法讀取。
- DOM 無法獲得。
- AJAX 請求不能發送。
跨域限制僅僅是瀏覽器的行爲,通過代理服務器,或者其他工具發送請求就能輕鬆繞過。
跨域時的動作
瀏覽器端
假設我們點擊一個按鈕去獲取數據,獲取數據的請求準備從瀏覽器發出,這時瀏覽器先會檢測這條請求是同源還是跨域,也就是與按鈕所在頁面的地址是同源還是跨域,如果是同源,好說,直接發送出去;如果是跨域的請求,那就得hold住先,瀏覽器會在請求的http header中加上一個Origin字段,標明這個請求是從哪裏發出來的,例如: Origin:http://neighbour.com:9000
。
服務器端
服務器收到請求會給與響應,響應的header裏寫明跨域的配置信息,告訴瀏覽器,它允許哪些域名發來的請求訪問,哪些method可以執行。瀏覽器收到響應後自動判斷能不能真正執行請求。
是否允許跨域的判定
跨域的判斷流程
瀏覽器先根據同源策略對前端頁面和後臺交互地址做匹配,若同源,則直接發送數據請求;若不同源,則發送跨域請求。
服務器解析程序收到瀏覽器跨域請求後,根據自身配置返回對應文件頭。若未配置過任何允許跨域,則文件頭裏不包含Access-Control-Allow-origin
字段,若配置過域名,則返回Access-Control-Allow-origin+ 對應配置規則裏的域名的方式
。
瀏覽器根據接受到的http文件頭裏的Access-Control-Allow-origin
字段做匹配,若無該字段,說明不允許跨域;若有該字段,則對字段內容和當前域名做比對,如果同源,則說明可以跨域,瀏覽器發送該請求;若不同源,則說明該域名不可跨域,不發送請求。
(但是不能僅僅根據服務器返回的文件頭裏是否包含Access-Control-Allow-origin
來判斷其是否允許跨域,因爲服務器端配置多域名跨域的時候,也會出現不能跨域的域名返回包裏沒有Access-Control-Allow-origin
字段的情況。)
一個支持CORS
的web服務器,有如下的判定字段,他們會在響應的header中寫明
Access-Control-Allow-Origin
:允許跨域的Origin
列表Access-Control-Allow-Methods
:允許跨域的方法列表Access-Control-Allow-Headers
:允許跨域的Header
列表Access-Control-Expose-Headers
:允許暴露給JavaScript
代碼的Header
列表Access-Control-Max-Age
:最大的瀏覽器緩存時間,單位爲s
其中Access-Control-Allow-Origin
(訪問控制之允許的源),在響應的http header中必須有的,表示允許訪問本服務器的源頭Origin
(域名),可以是特定的域名列表,用逗號分隔,也可以是通配符 *
,表示支持任意域名的訪問。
除了限定源頭Origin
,還會限制請求的方法Method
,Header
。
如,如果服務器設定Access-Control-Allow-Methods:GET
,那麼跨域的POST
請求無法在這個服務器執行。
流程
1.頁面發送請求
2.瀏覽器根據同源策略做出判定,如果是同源請求,直接發送出去;如果是跨域請求,在HTTP HEADER
加上Origin
字段,或是先發送一次預檢請求(preflight
)。
3.服務器接收請求,根據自身跨域的配置(如允許哪些域名,什麼樣的Method
訪問),返回文件頭。若未配置過任何允許跨域,則文件頭裏不包含Access-Control-Allow-origin
字段,若配置過域名,則返回Access-Control-Allow-origin+ 對應配置規則裏的域名的方式
。
瀏覽器接收到響應,根據響應頭裏的Access-Control-Allow-origin
字段做匹配,如果沒有這個字段,說明不匹配;如果有,將字段內容和當前域名做比對。如匹配,則可以發送請求。
跨域的請求形式
簡單請求;非簡單請求。
簡單請求,方法僅限於 HEAD,GET或POST,且Header的字段不超過以下字段:HeadeAccept
;
Accept-Language
;
Content-Language
;
Last-Event-ID
;
Content-Type
:只限於三個值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
(沒有application/json
, 說明如果發送JSON格式的body請求數據是一個非簡單請求)
非簡單請求就是其他請求。簡單請求瀏覽器會直接在請求的Header加上Origin字段再發送;非簡單請求瀏覽器則會先發送一次預檢請求,根據預檢請求的結果,決定是否正式發送請求。
參考鏈接:https://segmentfault.com/a/1190000003710973#item-2-3
https://segmentfault.com/a/1190000012163420
https://juejin.im/post/5c1df8d4f265da615d72a1e4