跨域的幾種方法

weixin_34378969發表於2017-06-17

什麼是跨域

說到跨域必須先解釋什麼是同源策略,它是由Netscape提出的一個著名的安全策略。瀏覽器出於安全方面的考慮,只允許與本域下的介面互動。不同源的客戶端指令碼在沒有明確授權的情況下,不能讀寫對方的資源。現在所有支援JavaScript 的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,埠相同。

首先我們來複習下一個完整的url組成

https://www.baidu.com:8080/aaa/1.html?id=10#name
window.location.portocol = https;   //協議
window.location.host = www.baidu.com:8080;   //域名
window.location.hostName = www.baidu.com;   
window.location.port = 8080;  //埠
window.location.search = ?id=10;
window.location.hash = #name;

還不是很熟悉的小夥伴可以參考下什麼是域名?什麼網站名?什麼是URL?,搞懂這個才能理解好同源策略和跨域。
舉例:

同源:
http://jirengu.com/a/b.js 和 http://jirengu.com/index.php 
不同源
http://jirengu.com/main.js 和 https://jirengu.com/a.php (協議不同)
http://jirengu.com/main.js 和 http://bbs.jirengu.com/a.php (域名不同,域名必須完全相同才可以)
http://jiengu.com/main.js 和 http://jirengu.com:8080/a.php (埠不同,第一個是80)

**需要注意的是: 對於當前頁面來說頁面存放的 JS 檔案的域不重要,重要的是載入該 JS 頁面所在什麼域 **

實際中我們經常需要到不同的域名下獲得資料,由於同源策略的存在我們無法通過AJAX等方式直接獲取資料 ,需要一些方法來實現跨域,主要的跨域方式有

  • jsonp
  • CORS (cross-origin-resource-shareing //iE10及以上支援)
  • 降域 (具有侷限性,只有是同屬於一個域名的二級域名還能夠使用這種方式)
  • postMessage

下面就具體介紹下每一種跨域方式

1、jsonp

JSONP 的原理是script標籤的src屬性不受同源策略影響,網頁通過新增一個元素,相當於向伺服器傳送了一個get請求,伺服器收到請求後,將資料放在一個指定名字的回撥函式裡傳回來。
舉一個簡單的例子
在目錄下建立個名稱為 1的txt檔案,內容為 123,在index.html中使用srcipt標籤請求文字檔案

5792743-b71cdcf823414ebe.png
檔案目錄
5792743-2ff89ee1fcd4b9aa.png
5792743-e1912f245b9ee22e.png

開啟控制檯檢視請求,在Response中我們成功的拿到1.txt中的資料123。但是,我們雖然拿到了資料但卻沒有辦法使用~!!!這時候有人就想到了用函式呼叫方式,將伺服器傳過來的資料以函式引數的方式呼叫

5792743-5dac8a4f57561b25.png
修改文字中的資料
5792743-9232a182a6f90d76.png

相當於這種寫法

5792743-26fcf890fc343dfb.png

這裡就相當於呼叫了函式fn,實際中呼叫的這個函式名也事先約定好的,傳送請求實際上就是利用DOM操作在文件中新增一個script標籤請求資料,注意這個請求的標籤在請求完成後要刪除

一個完整的JSONP例子

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>example</title>
    <style media="screen">
      .newsFrame{
        margin: 0 auto;
      }
    </style>
  </head>
  <body>
    <div class="newsFrame">
      <ul class="content">
        <li>上學</li>
        <li>消費</li>
        <li>生活</li>
      </ul>
      <button class="btn">換一批</button>
    </div>


     <script type="text/javascript">
        $('.btn').addEventListener('click',function(){
          var script = document.createElement('script');
          script.src='http://b.har.com:8080/getNews?callback=appendhtml';

          document.head.appendChild(script);
          document.head.removeChild(script);
        })

      function appendhtml(news){
        var html = '';
        for(var i=0; i<news.length; i++){
          html += '<li>' + news[i] + '</li>'
        }
        console.log(html)
      $('.content').innerHTML = html
      }

      function $(id){
        return document.querySelector(id)
      }

    </script>
  </body>
</html>

後端

   app.get('/getNews', function(req, res) {
        var news = [
        "看電視",
        '大學在學習',
        '大家在跑步',
        '天氣預報',
        '我要吃飯',
        '看電視'
      ]
      var data = [];
      for(var i=0; i<3; i++){
        var index = parseInt(Math.random()*news.length);
        data.push(news[index]);
      news.splice(index,1);
      }
    var cb = req.query.callback;
      res.send( cb + '(' + JSON.stringify(data) + ')' );
    });

最後,JS中還有其他三個標籤可以進行跨域請求img 、iframe、link(stylesheet)但他們都有缺陷

      img  
  //支援跨域但是無法實現獲取服務端返回的資料
  <iframe src="https://www.baidu.com"></iframe>
  //支援跨域,可以接收服務端資料,但是過程複雜
  <link rel="stylesheet" type="text/css" href="https://www.baidu.com">
  //會在CSS處理階段報錯

2、CORS

CORS全稱跨域資源共享(crossing-origin resourse sharing),是一種ajax跨域 請求資源的方式,支援現代瀏覽器,支援IE10以上。實現的方式很簡單,當你使用XMLHttpRequest傳送請求時,瀏覽器發現該請求不符合同源策略,會給該請求加一個請求頭:Origin,後臺進行一系列處理,如果確定接受請求則在返回的結果中加入一個響應頭:Access-Control-Allow-Origin;瀏覽器判斷響應頭中是否包含Origin的值,如果有則瀏覽器會處理響應,我們就可以拿到響應資料,如果不包含瀏覽器直接駁回,這時我們無法拿到響應資料。所以,CORS的表象是讓你覺得他與同源的AJAX的請求沒啥區別,程式碼完全一樣。

node的服務端

   app.get('/getNews', function(req, res) {
        var news = [
        "看電視",
        '大學在學習',
        '大家在跑步',
        '天氣預報',
        '我要吃飯',
        '看電視'
      ]
      var data = [];
      for(var i=0; i<3; i++){
        var index = parseInt(Math.random()*news.length);
        data.push(news[index]);
      news.splice(index,1);
      }
      res.header("Access-Control-Allow-Origin","http://a.jrg.com:8080");//CORS
      //res.header("Access-Control-Allow-Origin","*");
      res.send( data );
    });

深入學習可以參考阮一峰老師的部落格CORS

3、降域

降域主要應用在域名相同的網站之間的跨域。

舉個例子

http://a.jrg.com:8080/a.html
http://b.jrg.com:8080/a.html

根據同源策略上面的兩個網址是不是本域,但他們的域名相同,都是jrg.com所以可以通過降域的方式跨域 。具體的實現方法分別在對應的網頁的JS中加入

document.domain = "jrg.com";

4、postMessage

postMessage作為html5新引入的API允許來自不同源的指令碼採用非同步方式進行有限的通訊,可以實現跨文字檔、多視窗、跨域訊息傳遞。

語法

1、傳送

otherWindow.postMessage(message, targetOrigin, [transfer]);

  • otherWindow
    其他視窗的一個引用,比如iframe的contentWindow屬性、執行window.open返回的視窗物件、或者是命名過或數值索引的window.frames
  • message
    將要傳送到其他 window的資料。它將會被[結構化克隆演算法序列化。這意味著你可以不受什麼限制的將資料物件安全的傳送給目標視窗而無需自己序列化。
  • targetOrigin
    通過視窗的origin屬性來指定哪些視窗能接收到訊息事件,其值可以是字串""(表示無限制)或者一個URI。在傳送訊息的時候,如果目標視窗的協議、主機地址或埠這三者的任意一項不匹配targetOrigin提供的值,那麼訊息就不會被髮送;只有三者完全匹配,訊息才會被髮送。這個機制用來控制訊息可以傳送到哪些視窗;例如,當用postMessage傳送密碼時,這個引數就顯得尤為重要,必須保證它的值與這條包含密碼的資訊的預期接受者的orign屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道訊息應該傳送到哪個視窗,那麼請始終提供一個有確切值的targetOrigin,而不是。不提供確切的目標將導致資料洩露到任何對資料感興趣的惡意站點。
  • transfer
    可選是一串和message 同時傳遞的 Transferable
    物件. 這些物件的所有權將被轉移給訊息的接收方,而傳送一方將不再保有所有權。

2、監聽
執行如下程式碼, 其他window可以監聽派遣的message:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object.
  var origin = event.origin || event.originalEvent.origin; 
  if (origin !== "http://example.org:8080")
    return;

  // ...
}

message 的屬性有:

  • data
    從其他 window 中傳遞過來的物件。
  • origin
    呼叫 postMessage時訊息傳送方視窗的 origin。這個字串由 協議、“://“、域名、“ : 埠號”拼接而成。
  • source
    對傳送訊息的視窗物件的引用; 您可以使用此來在具有不同origin的兩個視窗之間建立雙向通訊。

相關文章