這里說的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" onload="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