持續更新地址:http://jaylin.wang/2017/cross...html
跨域HTTP請求是指當前文檔訪問其餘源提供的資源。兩個頁面具備相同的協議,域名和端口,則兩個頁面屬於相同的源,不一樣子域名之間也屬於不一樣的源。這是瀏覽器端的同源策略的約束,同源策略則是瀏覽器隔離潛在惡意文件的安全機制。git
目前經常使用的跨域解決方案有EmbedPing、JSONP、CORS。github
跨域限制是瀏覽器的一種行爲ajax
當咱們在瀏覽器的一個頁面中嘗試進行一次跨域操做,好比咱們在 localhost 的一個頁面經過 ajax 訪問 127.0.0.1 的一個路由,127.0.0.1已經將數據返回給瀏覽器,瀏覽器禁止了獲取響應數據行爲。咱們能夠看一張圖:json
跨域請求的幾種方式跨域
跨域寫
瀏覽器始終支持經過跨域寫操做。例如經過表單提交方式向其餘源提交數據,點擊連接重定向到其餘源。瀏覽器
跨域嵌入
瀏覽器始終支持跨域資源的嵌入。例如經過 img、script、video 等元素的 src 屬性嵌入跨域資源。緩存
跨域讀安全
這是我本身瞎取的一個名字,大體是利用瀏覽器支持標籤嵌入跨域屬性實現的一種方案。img、script、link[rel='stylesheet']會在頁面渲染過程當中請求其src/href設置的資源地址,咱們能夠在訪問的路由中作處理。例如使用img的src屬性:服務器
<script> var img = new Image() img.src = 'http://localhost:4000/check' </script>
由於沒法獲取到服務端返回的數據,EmbedPing是瀏覽器向服務器單向請求的過程。
咱們可使用 EmbedPing 作一些不嚴格統計。
JSONP也利用了 script 標籤的 src 屬性,瀏覽器會執行加載成功後的js。JSONP的侷限是隻能發送一個GET請求。
應用示例
看一個簡單示例,咱們但願在當前域的頁面上打印服務器的一個狀態,咱們使用JSONP能夠這樣實現,前能夠看一段簡單代碼(服務端使用koa構建的):
當前域屬於 http://localhost:3000
,咱們在頁面上這請求
<body> <script> var script = document.createElement('script') script.src = 'http://localhost:4000/jsonp/run' script.async = true document.body.appendChild(script) </script> </body>
在 http://localhost:4000/jsonp/run
中,咱們返回了一段打印狀態的script
var valForServerB = 'a' router.get('/jsonp/run', (ctx) => { let script = ` alert('我來自服務器b,個人值是${valForServerB}') ` ctx.body = script })
這樣,咱們能夠在當前頁面上顯示 valForServerB
的值了。
幾種類型
根據 JSONP 返回的代碼片斷不一樣,混入本身的情感,我將 JSONP 分爲三種類型(這種帶着情感的劃分,須要各位大牛的指正):
返回執行
返回代碼即執行代碼,直接在頁面上產生效果
返回定義
返回代碼是函數的定義,具體調用時機交給當前頁面決定
返回調用
函數的執行過程是在頁面上所定義的,JSONP的返回只是函數的調用,固然包含傳給函數的參數。
爲了保持當前頁面的的絕對控制權,返回調用應該是應用最廣的
錯誤處理
JSONP使用過程當中,會有兩種常見錯誤:
請求失敗或服務器返回失敗
捕獲此類錯誤,咱們是借用 script 的 onerror 處理
服務器返回內容錯誤
針對這類錯誤,咱們會在當前頁面借用定時器去處理。在返回調用時,咱們能夠在頁面上的函數定義中埋下時間因子;在返回定義時,咱們能夠設置時間點去檢測指望調用的方法是否存在。固然,此方法會由於網絡等因素變得不可靠。
CORS是當前頁面與其餘域執行的一種雙方約束,須要瀏覽器應用和服務器程序之間的協調。
咱們在服務器端能夠經過設置CORS響應頭部:
Access-Control-Allow-Origin: <origin> | *
容許訪問的源
Access-Control-Allow-Methods
容許訪問的方法
Access-Control-Allow-Headers
運行添加的請求頭部
Access-Control-Expose-Headers
容許客戶端能夠訪問的頭部
Access-Control-Max-Age
預請求的緩存週期
Access-Control-Allow-Credentials
是否運行請求加入用戶憑證信息
相應的,在請求頭部中,咱們能夠加入:
Origin
告知服務器請求的源
Access-Control-Request-Method
告知服務器使用請求的的方法
Access-Control-Request-Headers
告知服務器,請求中攜帶的頭部
跨域請求好,安全第一位。在咱們跨域請求時,咱們要確保咱們請求的服務器返回的數據是可信任的。