js處理的8種跨域方法

這裏說的js跨域是指通過js在不同的域之間進行數據傳輸或通信,比如用ajax向一個不同的域請求數據,或者通過js獲取頁面中不同域的框架中(iframe)的數據。只要協議、域名、端口有任何一個不同,都被當作是不同的域。

特別注意兩點:

#1、如果是協議和端口造成的跨域問題“前臺”是無能爲力的; 
#2、在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。

要解決跨域的問題,我們可以使用以下幾種方法:

方法一、通過jsonp跨域

JSONP包含兩部分:回調函數和數據。 
回調函數:當響應到來時要放在當前頁面被調用的函數。 
數據:就是傳入回調函數中的json數據,也就是回調函數的參數了。

/*handleResonse({"data": "zhe"})*/
//原理如下:
//當我們通過script標籤請求時
//後臺就會根據相應的參數(json,handleResponse)
//來生成相應的json數據(handleResponse({"data": "zhe"}))
//最後這個返回的json數據(代碼)就會被放在當前js文件中被執行
//至此跨域通信完成
//1、使用JS動態生成script標籤,進行跨域操作
function handleResponse(response){
    console.log('The responsed data is: '+response.data);
    //處理獲得的Json數據
}
var script = document.createElement('script');
script.src = 'http://www.example.com/data/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);
--------------------------
//2、手動生成script標籤
function handleResponse(response){
    console.log('The responsed data is: '+response.data);
    //處理獲得的Json數據
}
<script src="http://www.example.com/data/?callback=handleResponse"></script>
--------------------------
//3、使用jQuery進行jsonp操作
//jquery會自動生成一個全局函數來替換callback=?中的問號,之後獲取到數據後又會自動銷燬
//$.getJSON方法會自動判斷是否跨域,不跨域的話,就調用普通的ajax方法;跨域的話,則會以異步加載js文件的形式來調用jsonp的回調函數。
<script>
    $.getJson('http://www.example.com/data/?callback=?',function(jsondata){
    //處理獲得的Json數據
});
</script>

jsonp雖然很簡單,但是有如下缺點:

#1)安全問題(請求代碼中可能存在安全隱患)

#2)要確定jsonp請求是否失敗並不容易

方法二、通過document.domain+iframe來跨子域(只有在主域相同的時候才能使用該方法)

瀏覽器同源策略限制:

#(1)不能通過ajax的方法去請求不同源中的文檔。
#(2)瀏覽器中不同域的框架之間是不能進行js的交互操作的。

所以,在不同的框架之間(父子或同輩),是能夠獲取到彼此的window對象的,但不能使用獲取到的window對象的屬性和方法(html5中的postMessage方法是一個例外),總之,你可以當做是隻能獲取到一個幾乎無用的window對象。
  例如,在一個頁面 http:// www.example.com/a.html 中,有一個iframe框架它的src是http:// example.com/b.html, 很顯然,這個頁面與它裏面的iframe框架是不同域的,所以我們是無法通過在頁面中書寫js代碼來獲取iframe中的東西的:

 1 //  http://www.example.com/a.html 頁面中
 2 <script>
 3 function onLoad(){
 4     var iframe = document.getElementById('iframe');
 5     var win = iframe.contentWindow;
 6     //這裏能夠獲取到iframe中的window對象,但是window對象的屬性和方法幾乎不可用。
 7     var doc = win.document;//這裏獲取不到iframe中的document對象
 8     var name = win.name;//這裏獲取不到window對象的name屬性
 9     ······
10 }
11 <iframe id = "iframe" src ="http:// example.com/b.html" onload = "onLoad()"></iframe> 

所以我們就要用到document.domain

1) 在頁面http:// www.a.com/dir/a.html中設置document.domain:

1 <iframe src = "http://script.a.com/dir/b.html" id="iframe" onload = "loLoad()"></iframe>
2 <script>
3 document.domain = "a.com";//設置成主域
4 function test(){
5     var iframe = document.getElementById("iframe");
6     var win = iframe.contentWindow;
7     //在這裏就可以操作b.html
8 }
9 </script>

2) 在http:// script.a.com/dir/b.html也需要顯示的設置document.domain

1 <script>
2     document.domain = "a.com";
3 </script>

注意,document.domain的設置是有限制的:

  我們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。
  例如:a.b.c.com 中某個文檔的document.domain 可以設成a.b.c.com、b.c.com 、c.com中的任意一個

方法三、使用window.name+iframe來進行跨域

window的name屬性特徵:name 值在不同的頁面(甚至不同域名)加載後依舊存在,並且可以支持非常長的 name 值(2MB),即在一個窗口(window)的生命週期內,窗口載入的所有的頁面都是共享一個window.name的,每個頁面window.name都有讀寫的權限。

  正是由於window的name屬性的特徵,所以可以使用window.name來進行跨域。
  舉例:
  1)在一個a.html頁面中,有如下代碼:

1 <script>
2     window.name = "哈哈,我是頁面a設置的值喲!";
3     //設置window.name的值
4     setTimeout(function(){
5         window.location = 'b.html';
6     },3000);//3秒後把一個新頁面載入當前window
7 </script>

2)再在b.html中讀取window.name的值:

1 <script>
2     alert(window.name);//讀取window.name的值
3 </script>

3)a.html載入3秒後,跳轉到b.html頁面中,結果爲

#注意: 
#1.window.name的值只能是字符串的形式,這個字符串的大小最大能允許2M左右甚至更大的一個容量,具體取決於不同的瀏覽器。

接下來使用window.name進行跨域舉例

  比如:有一個example.com/a.html頁面,需要通過a.html頁面裏的js來獲取另一個位於不同域上的頁面cnblogs.com/data.html裏的數據。
  1)創建cnblogs.com/data.html代碼:

1 <script>
2     window.name = "我是data.html的數據,所有可以轉化爲字符串來傳遞的數據都可以在這裏使用,比如這裏可以傳遞Json數據";
3 </script>

2)創建example.com/a.html的代碼:
  想要即使a.html頁面不跳轉也能得到data.html裏的數據。在a.html頁面中使用一個隱藏的iframe來充當一箇中間人角色,由iframe去獲取data.html的數據,然後a.html再去得到iframe獲取到的數據。

 1 <script>
 2     function getData(){
 3     //iframe載入data.html頁面會執行此函數
 4         var ifr = document.getElementById("iframe");
 5         ifr.onload = function(){
 6         //這個時候iframe和a.html已經處於同一源,可以互相訪問
 7             var data = ifr.contentWindow.name;
 8 //獲取iframe中的window.name,也就是data.html中給它設置的數據
 9             alert(data);
10         }
11         ifr.src = 'b.html';//這裏的b.html爲隨便一個頁面,只要與a.html同源就行,目的是讓a.html能夠訪問到iframe中的東西,否則訪問不到
12     }
13 </script>
14 <iframe id = "iframe" src = "cnblogs.com/data.html" style = "display:none" onload = "getData()"></iframe>

方法四、使用window.postMessage方法來跨域(不常用)

 window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬於同源或不同源(可實現跨域),目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
  message:爲要發送的消息,類型只能爲字符串;
  targetOrigin:用來限定接收消息的那個window對象所在的域,如果不想限定域,可以使用通配符 “*”。

1)創建www.test.com/a.html頁面代碼:

1 <script>
2 function onLoad(){
3     var iframe = document.getElementById("iframe");
4     var win = iframe.contentWindow;
5     win.postMessage('哈哈,我是來自頁面a.html的信息喲!','*');//向不同域的www.script.com/b.html發送消息
6 }
7 </script>
8 <iframe id="iframe" src="www.script.com/b.html" οnlοad="onLoad()"></iframe>

2)創建www.script.com/b.html頁面代碼:

1 <script>
2 window.onmessage = function(e){//註冊message時間來接收消息
3     e = e || event;            //獲取時間對象
4     alert(e.data);             //通過data屬性來得到傳送的消息
5 }
6 </script>

優點:使用postMessage來跨域傳送數據還是比較直觀和方便的; 
缺點: IE6、IE7不支持,所以用不用還得根據實際需要來決定。

方法五、使用跨域資源共享(CORS)來跨域

CORS:一種跨域訪問的機制,可以讓AJAX實現跨域訪問;CORS允許一個域上的網絡應用向另一個域提交跨域AJAX請求。
服務器設置Access-Control-Allow-Origin HTTP響應頭之後,瀏覽器將會允許跨域請求.
就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,還是應該失敗。

1) IE中對CORS的實現是通過xdr

1 var xdr = new XDomainRequest();
2 xdr.onload = function(){
3     console.log(xdr.responseText);
4 }
5 xdr.open('get', 'http://www.test.com');
6 ......
7 xdr.send(null);

2) 其它瀏覽器中的實現就在xhr中

 1 var xhr =  new XMLHttpRequest();
 2 xhr.onreadystatechange = function () {
 3   if(xhr.readyState === 4 && xhr.status === 200){
 4         console.log(xhr.responseText);
 5         } 
 6     }
 7 }
 8 xhr.open('get', 'http://www.test.com');
 9 ......
10 xhr.send(null);

3) 實現跨瀏覽器的CORS

 1 function createCORS(method, url){
 2     var xhr = new XMLHttpRequest();
 3     if('withCredentials' in xhr){
 4         xhr.open(method, url, true);
 5     }else if(typeof XDomainRequest != 'undefined'){
 6         var xhr = new XDomainRequest();
 7         xhr.open(method, url);
 8     }else{
 9         xhr = null;
10     }
11     return xhr;
12 }
13 var request = createCORS('get', 'http://www.test.com');
14 if(request){
15     request.onload = function(){
16         ......
17     };
18     request.send();
19 }

方法六、使用location.hash+iframe來跨域(不常用)

假設域名test.com下的文件a.html要和csdnblogs.com域名下的b.html傳遞信息。
  1) 創建test.com下的a.html頁面, 同時在a.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一旦有變化則獲取獲取hash值,代碼如下:

<script>
function startRequest(){
    var ifr = document.createElement('iframe');
    //創建一個隱藏的iframe
    ifr.style.display = 'none';
    ifr.src = 'http://www.csdnblogs.com/b.html#paramdo';
    //傳遞的location.hash 
    document.body.appendChild(ifr);
}

function checkHash() {
    try {
        var data = location.hash ? location.hash.substring(1):'';
        if (console.log) {
            console.log('Now the data is ' + data);
        }
    } catch (e) {};
}
setInterval(checkHash, 5000);
window.onload = startRequest;
</script>

2) b.html響應請求後再將通過修改a.html的hash值來傳遞數據,代碼如下:

<script>
function checkHash() {
    var data = '';
    //模擬一個簡單的參數處理操作
    switch (location.hash) {
        case '#paramdo':
            data = 'somedata';
            break;
        case '#paramset':
             //do something……
            break;
        default:
            break;
    }
    data && callBack('#' + data);
}

function callBack(hash) {
    // ie、chrome的安全機制無法修改parent.location.hash
    //所以要利用一箇中間的www.csdnblogs.com域下的代理iframe
    var proxy = document.createElement('iframe');
    proxy.style.display = 'none';
    proxy.src = 'http://www.csdnblogs.com/c.html' + hash; 
    // 注意該文件在"www.csdnblogs.com"域下
    document.body.appendChild(proxy);
}
window.onload = checkHash;
</script>

3) test.com域下的c.html代碼:

1 <script>
2 //因爲parent.parent和自身屬於同一個域,所以可以改變其location.hash的值
3 parent.parent.location.hash = self.location.hash.substring(1);
4 </script>

方法七、使用Web sockets來跨域

web sockets: 是一種瀏覽器的API,它的目標是在一個單獨的持久連接上提供全雙工、雙向通信。(同源策略對web sockets不適用)

web sockets原理:在JS創建了web socket之後,會有一個HTTP請求發送到瀏覽器以發起連接。取得服務器響應後,建立的連接會使用HTTP升級從HTTP協議交換爲web sockt協議。

1 <script>
2 var socket = new WebSockt('ws://www.test.com');
3 //http->ws; https->wss
4 socket.send('hello WebSockt');
5 socket.onmessage = function(event){
6     var data = event.data;
7 }

方法八、使用flash URLLoader來跨域

flash有自己的一套安全策略,服務器可以通過crossdomain.xml文件來聲明能被哪些域的SWF文件訪問,SWF也可以通過API來確定自身能被哪些域的SWF加載。
例如:當跨域訪問資源時,例如從域baidu.com請求域google.com上的數據,我們可以藉助flash來發送HTTP請求。

跨域實現方式:
 

#1. 首先,修改域google.com上的crossdomain.xml(一般存放在根目錄,如果沒有需要手動創建) ,把baidu.com加入到白名單。 
#2. 其次,通過Flash URLLoader發送HTTP請求 
#3. 最後,通過Flash API把響應結果傳遞給JavaScript。

 

Flash URLLoader是一種很普遍的跨域解決方案,不過需要支持iOS的話,這個方案就不可行了。 

以上八種方法,可以根據項目的實際情況來進行選擇應用,個人認爲window.name的方法既不復雜,也能兼容到幾乎所有瀏覽器,這真是極好的一種跨域方法。

 

原文鏈接:https://blog.csdn.net/wangchengiii/article/details/78081032

轉載於:https://www.cnblogs.com/lcspring/p/11079754.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章