實現跨域的N種方法

從域提及

域: 域是WIN2K網絡系統的安全性邊界。咱們知道一個計算機網最基本的單元就是「域」,這一點不是WIN2K所獨有的,但活動目錄能夠貫穿一個或多個域。在獨立的計算機上,域即指計算機自己,一個域能夠分佈在多個物理位置上,同時一個物理位置又能夠劃分不一樣網段爲不一樣的域,每一個域都有本身的安全策略以及它與其餘域的信任關係。當多個域經過信任關係鏈接起來以後,活動目錄能夠被多個信任域域共享javascript

域樹:域樹由多個域組成,這些域共享同一表結構和配置,造成一個連續的名字空間。樹中的域經過信任關係鏈接起來,活動目錄包含一個或多個域樹。域樹中的域是經過雙向可傳遞信任關係鏈接在一塊兒。因爲這些信任關係是雙向的並且是可傳遞的,所以在域樹或樹林中新建立的域能夠當即與域樹或樹林中每一個其餘的域創建信任關係。這些信任關係容許單一登陸過程,在域樹或樹林中的全部域上對用戶進行身份驗證,但這不必定意味着通過身份驗證的用戶在域樹的全部域中都擁有相同的權利和權限。由於域是安全界限,因此必須在每一個域的基礎上爲用戶指派相應的權利和權限。php

域樹中的域層次越深級別越低,一個「.」表明一個層次。
如域zhidao.baidu.com(百度知道)就比 baidu.com(百度)這個域級別低,由於它有兩個層次關係,而baidu.com只有一個層次。html

何爲跨域

默認狀況下,,XHR 對象只能訪問與包含它的頁面位於同一個域中的資源。這種安全策略能夠預防某些惡意行爲。可是,實現合理的跨域請求對開發某些瀏覽器應用程序也是相當重要的。html5

只要協議、域名、端口有任何一個不一樣,都被看成是不一樣的域java

好比在http://www.a.com/a.js 頁面向如下頁面發送一個ajax請求,如下是其請求結果及說明ajax

URL 說明 是否容許通訊
http://www.a.com/b.js 同一域名下 容許
http://www.a.com:8000/a.js 同一域名,不一樣端口 不容許
https://www.a.com/b.js 同一域名,不一樣協議 不容許
http://script.a.com/b.js 主域相同,子域不一樣 不容許
http://a.com/b.js 同一域名,不一樣二級域名 不容許
http://www.a.com/b.js 不一樣域名 不容許

對於端口和協議的不一樣,只能經過後臺來解決。咱們要解決的是域名不一樣的問題json

如何跨域

(一) CORS(Cross-Origin Resource Sharing,跨源資源共享)跨域

1.CORS(Cross-Origin Resource Sharing,跨源資源共享)是W3C 的一個工做草案,定義了在必須訪問跨源資源時,瀏覽器與服務器應該如何溝通。CORS 背後的基本思想,就是使用自定義的HTTP 頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。
2.實現此功能很是簡單,只需由服務器發送一個響應標頭便可。瀏覽器

瀏覽器支持狀況:安全

  • IE 8+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Chrome 3+

假設咱們頁面或者應用已在 http://www.a.com/ 上了,而咱們打算從 http://www.b.com 請求提取數據。通常狀況下,若是咱們直接使用 AJAX 來請求將會失敗,瀏覽器也會返回錯誤。
利用 CORS,http://www.b.com 只需添加一個標頭,就能夠容許來自 http://www.a.com 的請求。
下面是用php進行的設置,「*」號表示容許任何域向咱們的服務端提交請求:

header{"Access-Control-Allow-Origin: *"}

CORS的兼容性寫法

function createCORSRequest(method, url){
        var xhr = new XMLHttpRequest();
        //非IE瀏覽器
        if ("withCredentials" in xhr){
            xhr.open(method, url, true);
        //IE瀏覽器
        } else if (typeof XDomainRequest != "undefined"){
            vxhr = new XDomainRequest();
            xhr.open(method, url);
        } else {
            xhr = null;
        }
        return xhr;
    }
    var request = createCORSRequest("get", "http://www.somewhere-else.com/page/");
    if (request){
        request.onload = function(){
            //對request.responseText 進行處理
        };
        request.send();
    }

(二) JSONP(JSON with Padding 填充式JSON 或參數式JSON)

在js中,咱們雖然不能直接用XMLHttpRequest請求不一樣域上的數據時,可是在頁面上引入不一樣域上的js腳本文件倒是能夠的,jsonp正是利用這個特性來實現的

JSONP由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數,而數據就是傳入回調函數中的JSON數據。

例如:

<script type="text/javascript">
    function dosomething(jsondata){
        //處理得到的json數據
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
  • 首先第一個script便籤訂義了一個處理數據的函數;
  • 而後第二個script標籤載入一個js文件,http://example.com/data.php 是數據所在地址,可是由於是當作js來引入的,因此http://example.com/data.php 返回的必須是一個能執行的js文件;
  • 最後js文件載入成功後會執行咱們在url參數中指定的函數,而且會把咱們須要的json數據做爲參數傳入。因此php應該是這樣的
<?php
    $callback = $_GET['callback'];//獲得回調函數名
    $data = array('a','b','c');//要返回的數據
    echo $callback.'('.json_encode($data).')';//輸出
    ?>

最終,輸出結果爲:dosomething(['a','b','c']);
從上面能夠看出jsonp是須要服務器端的頁面進行相應的配合的。

JSONP的優缺點
優勢:

  • 它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;
  • 可以直接訪問響應文本,支持在瀏覽器與服務器之間雙向通訊

缺點:

  • JSONP 是從其餘域中加載代碼執行。若是其餘域不安全,極可能會在響應中夾帶一些惡意代碼,而此時除了徹底放棄JSONP 調用以外,沒有辦法追究。所以在使用不是你本身運維的Web 服務時,必定得保證它安全可靠。
  • 它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript調用的問題。

(三) window.name

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

這裏有三個頁面:

a.com/app.html:應用頁面。
a.com/proxy.html:代理文件,通常是一個沒有任何內容的html文件,須要和應用頁面在同一域下。
b.com/data.html:應用頁面須要獲取數據的頁面,可稱爲數據頁面。

app.html

<iframe src="b.com/data.html" id="iframe"></iframe>
<script>
    var iframe = document.getElementById("iframe");
    iframe.src = "a.com/proxy.html";//這是一個與a.com/app.html同源的頁面
    iframe.onload = function(){
        var data = iframe.contentWindow.name; //取到數據
    }

</script>

data.html

<script>
    // 這裏是要傳輸的數據,大小通常爲2M,IE和firefox下能夠大至32M左右
    // 數據格式能夠自定義,如json、字符串
    window.name = "數據"
</script>

iframe首先的地址是b.com/data.html,因此能取到window.name數據;
可是iframe如今跟app.html並不一樣源,app.html沒法獲取到數據,因此又將iframe的連接跳轉至a.com/proxy.html這個代理頁面,如今app.html跟iframe就同源了。

注意:iframe由b.com/data.html跳轉到a.com/proxy.html頁面,window.name的value是不變的

獲取數據之後銷燬這個iframe,釋放內存;這也保證了安全(不被其餘域frame js訪問)

<script type="text/javascript">
    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
</script>

(四) document.domain + iframe

對於主域相同而子域不一樣的例子,能夠經過設置document.domain的辦法來解決。
具體的作法是能夠在http://www.a.com/a.html 和http://script.a.com/b.html 兩個文件中分別設置document.domain = 'a.com',而後經過a.html文件中建立一個iframe,去控制iframe的contentDocument,這樣兩個js文件之間就能夠「交互」了。
http://www.a.com/a.html頁面

<iframe src="http://script.a.com/b.html" frameborder="0"></iframe>
<script>
    document.domain = 'a.com';
</script>

http://script.a.com/b.html頁面

<script>
    document.domain = 'a.com';
</script>

這樣倆個頁面就能夠經過js相互訪問各類屬性和對象了。

document.domain的設置是有限制的,咱們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。例如:a.b.example.com 中某個文檔的document.domain 能夠設成a.b.example.com、b.example.com 、example.com中的任意一個,可是不能夠設成 c.a.b.example.com,由於這是當前域的子域,也不能夠設成baidu.com,由於主域已經不相同了。


(五) HTML5的window.postMessage

window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
window.postMessage容許兩個窗口/幀之間跨域發送數據消息。從本質上講,window.postMessage是一個跨域的無服務器墊片的Ajax。

用法:
otherWindow.postMessage(message, targetOrigin);

  • otherWindow: 對接收信息頁面的window的引用。能夠是頁面中iframe的contentWindow屬性;window.+open的返回值;經過name或下標從window.frames取到的值。
  • message: 所要發送的數據,string類型。
  • targetOrigin: 用於限制otherWindow,「*」表示不做限制

數據發送端
a.com/index.html中的代碼:

<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 設定接收端的域,*則爲不限制
                                       
    ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>

數據接收端
b.com/index.html中的代碼:

<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 經過origin屬性判斷消息來源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 彈出"I was there!"
            alert(event.source);  // 對a.com、index.html中window對象的引用
                                  // 但因爲同源策略,這裏event.source不能夠訪問window對象
        }
    }, false);
</script>

參考文章:JavaScript跨域總結與解決辦法js中幾種實用的跨域方法原理詳解

腦細胞已經陣亡( ⊙ o ⊙ )

若是以爲本文不錯的話,幫忙點擊下面的推薦哦

相關文章
相關標籤/搜索