1. 程式人生 > >什麼是跨域以及幾種簡單解決方案

什麼是跨域以及幾種簡單解決方案

要明白什麼是跨域之前,首先要明白什麼是同源策略

同源策略就是用來限制從一個源載入的文件或指令碼與來自另一個源的資源進行互動。那怎樣判斷是否是同源呢?

如果協議,埠(如果指定了)和主機對於兩個頁面是相同的,則兩個頁面具有相同的源,也就是同源。也就是說,要同時滿足以下3個條件,才能叫同源:

  1. 協議相同
  2. 埠相同
  3. 主機相同

舉個例子就一目瞭然了:

我們來看下面的頁面是否與 http://store.company.com/dir/index.html 是同源的?

  1. http://store.company.com/dir/index2.html 同源
  2. http://store.company.com/dir2/index3.html 同源 雖然在不同資料夾下
  3. https://store.company.com/secure.html 不同源 不同的協議(https)
  4. http://store.company.com:81/dir/index.html 不同源 不同的埠(81)
  5. http://news.company.com/dir/other.html 不同源 不同的主機(news)

所以當面對跨域問題的時候,有什麼解決方案呢?

跨域的幾種解決方案

document.domain方法

我們來看一個具體場景:有一個頁面 http://www.example.com/a.html ,它裡面有一個iframe,這個iframe的源是 http://example.com/b.html ,很顯然它們是不同源的,所以我們無法在父頁面中操控子頁面的內容。

解決方案如下:

<!-- b.html -->
<script>
document.domain = 'example.com';
</script>
<!-- a.html -->
<script>
document.domain = 'example.com';
var iframe = document.getElementById('iframe').contentWindow.document;

//後面就可以操作iframe裡的內容了...

</script>

所以我們只要將兩個頁面的document.domain設定成一致就可以了,要注意的是,document.domain的設定是有限制的,我們只能把document.domain設定成自身或更高一級的父域。

但是,這種方法只能解決主域相同的跨域問題。

window.name方法

window物件有個name屬性,該屬性有個特徵:即在一個視窗(window)的生命週期內,視窗載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的許可權,window.name是持久存在一個視窗載入過的所有頁面中的,並不會因新頁面的載入而進行重置。

我們來看一個具體場景,在一個頁面 example.com/a.html 中,我們想獲取 data.com/data.html 中的資料,以下是解決方案:

<!-- data.html -->
<script>
window.name = 'data'; //這是就是我們需要通訊的資料
</script>
<!-- a.html -->
<html>
<head>
<script>
    function getData () {
        var iframe = document.getElementById('iframe');
        iframe.src = 'example.com/b.html'; // 這裡讓iframe與父頁面同源
        
        iframe.onload = function () {
            var data = iframe.contentWindow.name; //在這裡我們得到了跨域頁面中傳來的資料
        };
    }
</script>
</head>
<body>
</body>
</html>

JSONP方法

JONSP(JSON with Padding)是JSON的一種使用模式。基本原理如下:

<!-- a.html -->
<script>
    function dealData (data) {
        console.log(data);
    }
</script>

<script src='http://example.com/data.php?callback=dealData'></script>
<?php
    $callback = $_GET['callback'];
    $data = 'data';
    echo $callback.'('.json_encode($data).')';
?>

這時候在a.html中我們得到了一條js的執行語句dealData('data'),從而達到了跨域的目的。

所以JSONP的原理其實就是利用引入script不限制源的特點,把處理函式名作為引數傳入,然後返回執行語句,仔細閱讀以上程式碼就可以明白裡面的意思了。

如果在jQuery中用JSONP的話就更加簡單了:

<script>
$.getJSON(''http://example.com/data.php?callback=?', function (data) {
    console.log(data);
});
</script>

注意jQuery會自動生成一個全域性函式來替換callback=?中的問號,之後獲取到資料後又會自動銷燬,實際上就是起一個臨時代理函式的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就呼叫普通的ajax方法;跨域的話,則會以非同步載入js檔案的形式來呼叫JSONP的回撥函式。

總結

除了上述方法外,HTML5還新增了一個window.postMessage()方法,有興趣的可以自行查閱。

最後,解決跨域問題還有一個更通用更強大的CORS方法,我單獨把它拿出來總結了一篇文章:跨域問題的根本解決方案CORS