實現跨域的N種方法


從域說起

域: 域是WIN2K網絡系統的安全性邊界。我們知道一個計算機網最基本的單元就是“域”,這一點不是WIN2K所獨有的,但活動目錄可以貫穿一個或多個域。在獨立的計算機上,域即指計算機本身,一個域可以分布在多個物理位置上,同時一個物理位置又可以划分不同網段為不同的域,每個域都有自己的安全策略以及它與其他域的信任關系。當多個域通過信任關系連接起來之后,活動目錄可以被多個信任域域共享

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

域樹中的域層次越深級別越低,一個“.”代表一個層次。
如域zhidao.baidu.com (百度知道)就比 baidu.com(百度)這個域級別低,因為它有兩個層次關系,而baidu.com只有一個層次。

何為跨域

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

只要協議、域名、端口有任何一個不同,都被當作是不同的域

比如在http://www.a.com/a.js 頁面向以下頁面發送一個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 不同域名 不允許

對於端口和協議的不同,只能通過后台來解決。我們要解決的是域名不同的問題

如何跨域

(一) 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 ⊙ )

如果覺得本文不錯的話,幫忙點擊下面的推薦哦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM