九種方式實現跨域

jfaWei發表於2019-02-26
  1. 什麼是跨域
    即‘同源策略’。簡單說,就是瀏覽器規定的安全功能。(自行Google,百度看看,因為我也一知半解)“協議+域名+埠”三者都相同,才同源,反著任意一個不同則跨域。同源策略限制的內容有(即同源才可以訪問,跨域則不行):
    (1).Cookie,LocalStorage,IndexedDB等的儲存內容
    (2).DOM節點。
    (3)ajax請求傳送後,返回結果被瀏覽器攔截。
    三個允許跨域載入的標籤:
     (1)<img src="xxxx">
       (2)<link href="xxxx">
        (3)<script src="xxx">

    注意:跨域並不會請求發不出去,請求能傳送出去。服務端能收到請求並能正常返回結果,只是結果被瀏覽器攔截了。
    2.跨域解決方法

    1. jsonp
      利用script標籤沒有跨域限制的漏洞
      網頁可以得到其他源動態產生的JSON資料。JSONP請求一定要對方的伺服器做支援。只能get方法。
      實現步驟可以自行google,百度。簡單說,宣告一個回撥函式傳給跨域伺服器,伺服器返回資料時帶上回撥函式 並做特殊拼接:回撥函式(‘要跨域獲取的目標資料’),客戶端宣告這個回撥函式的具體實現邏輯,當跨域伺服器返回這個回撥函式和目標資料的拼接時(即呼叫這個回撥函式),則可以操作目標資料了,並獲取。
      2.cors
      需要瀏覽器和後端同時支援.ie8和ie9需要通過XDomainRequest(啥東西,我不懂)來實現。
      伺服器設定了Access-Control-Allow-Origin就可以開啟CORS。該屬性表示哪些域名可以訪問資源,如果設定萬用字元則表示所有網站都可以訪問。
      前端分為簡單請求和複雜請求
      簡單請求:get,head,post同時Content-Type僅限text/plain,multipart/form-data,application/x-www-form-urlencoded
      複雜請求:不是簡單的就是複雜的。複雜請求在正式通訊前會增加一次HTTP查詢請求即預檢請求,請求方法為option來知道伺服器是否允許跨域請求。
      3.postMessage
      postMessage是Html5 XMLHttpRequest level2中的API,是為數不多的跨域操作的window屬性之一。作用:
      (1)頁面和它開啟的新視窗的資料傳遞。
      (2)多個視窗之間的訊息傳遞
      (3)頁面和巢狀iframe訊息的傳遞
      (4)上面三個場景的跨域資料傳遞
      postMessage()方法允許來自不同源的指令碼採用非同步方式進行的有限通訊,可以實現跨文字檔,多視窗,跨域訊息的傳遞
      實現:otherWindow.postMessage(‘messsage’,‘targetOrigin’,‘[transfer]’)
      message:是將要傳送到其他window的資料。
      targetOrigin:通過視窗的origin屬性來指定哪些視窗能接受到訊息事件,其值可以是‘*’表示無限制或者一個url。(協議,主機,地址或埠必須和目標視窗一致)
      transfer:是一串和message同時傳送的Tranferable物件,這些物件的所有權將轉移給訊息接受方,而傳送方將步不再保有所有權。(不懂)
         // a.html
      <iframe src="b.html" frameborder="0" id="frame" onload="load()"></iframe//等它載入完觸發一個事件
      //內嵌在a.html
      <script>
      function load() {
      let frame = document.getElementById('frame')
      frame.contentWindow.postMessage('我愛你', 'localhost:4000') //傳送資料
      window.onmessage = function(e) { //接受返回資料
        console.log(e.data) //我不愛你
      }
      }
      </script>`
      `// b.html
      window.onmessage = function(e) {
      console.log(e.data) //我愛你
      e.source.postMessage('我不愛你', e.origin)
      }

      4.websocket(自己查)
      5.Node中間代理(兩次跨域)
      原理:同源策略是瀏覽器需要遵循的標準,而如果是伺服器向伺服器請求就無需遵循了。
      a.接受客戶端請求。(代理伺服器)
      b.將請求轉發給伺服器。
      c.拿到伺服器響應資料(代理伺服器)
      d.將資料轉發給客戶端
      6.nginx 反向代理
      7.window.name + iframe
      window.name 屬性的獨特之處:name 值在不同的頁面(甚至不同域名)載入後依舊存在,並且可以支援非常長的 name 值(2MB)。

      // a.html(http://localhost:3000/b.html)
      <iframe src="c.html" frameborder="0" onload="load()" id="iframe"></iframe>
      <script>
      let first = true
      // onload事件會觸發2次,第1次載入跨域頁,並留存資料於window.name
      function load() {
      if(first){
      // 第1次onload(跨域頁)成功後,切換到同域代理頁面
      let iframe = document.getElementById('iframe');
      iframe.src = /b.html';
      first = false;
      }else{
      // 第2次onload(同域b.html頁)成功後,讀取同域window.name中資料
      console.log(iframe.contentWindow.name);
      }
      }
      </script>
      // c.html(http://localhost:4000/c.html)
      <script>
      window.name = '我不愛你'
      </script>

      8.location.hash + iframe
      實現原理: a.html 欲與 c.html 跨域相互通訊,通過中間頁 b.html 來實現。 三個頁面,不同域之間利用 iframe 的 location.hash 傳值,相同域之間直接 js 訪問來通訊。

具體實現步驟:一開始 a.html 給 c.html 傳一個 hash 值,然後 c.html 收到 hash 值後,再把 hash 值傳遞給 b.html,最後 b.html 將結果放到 a.html 的 hash 值中。
同樣的,a.html 和 b.html 是同域的

// a.html
  <iframe src="c.html#iloveyou"></iframe>
  <script>
    window.onhashchange = function () { //檢測hash的變化
      console.log(location.hash);
    }
  </script>
 // b.html
  <script>
    window.parent.parent.location.hash = location.hash
    //b.html將結果放到a.html的hash值中,b.html可通過parent.parent訪問a.html頁面
  </script>
 // c.html
 console.log(location.hash);
  let iframe = document.createElement('iframe');
  iframe.src = 'b.html#idontloveyou';
  document.body.appendChild(iframe);

9.document.domain + iframe
該方式只能用於二級域名相同的情況下,比如 a.test.com 和 b.test.com 適用於該方式。
只需要給頁面新增 document.domain ='test.com' 表示二級域名都相同就可以實現跨域。

實現原理:兩個頁面都通過 js 強制設定 document.domain 為基礎主域,就實現了同域。

// a.html
<body>
 helloa
  <iframe src="b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>
// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn
     var a = 100;
   </script>
</body>

總結
CORS 支援所有型別的 HTTP 請求,是跨域 HTTP 請求的根本解決方案
JSONP 只支援 GET 請求,JSONP 的優勢在於支援老式瀏覽器,以及可以向不支援 CORS 的網站請求資料。
不管是 Node 中介軟體代理還是 nginx 反向代理,主要是通過同源策略對伺服器不加限制。
日常工作中,用得比較多的跨域方案是 cors 和 nginx 反向代理

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章