前面的話
script、image、iframe的src都不受同源策略的影響。所以可以借助這一特點,實現跨域。例如,前面介紹的jsonp是使用script標簽,imgPing是使用image標簽,而本文將介紹使用iframe標簽實現跨域
引入
1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實行這個政策。最初,它的含義是指,A 網頁設置的 Cookie,B 網頁不能打開,除非這兩個網頁“同源”。所謂“同源”指的是”三個相同“:1、協議相同;2、域名相同;3、端口相同
舉例來說,http://www.example.com/dir/page.html
這個網址,協議是http://
,域名是www.example.com
,端口是80
(默認端口可以省略)。它的同源情況如下
http://www.example.com/dir2/other.html:同源 http://example.com/dir/other.html:不同源(域名不同) http://v2.www.example.com/dir/other.html:不同源(域名不同) http://www.example.com:81/dir/other.html:不同源(端口不同) https://www.example.com/dir/page.html:不同源(協議不同)
同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。
設想這樣一種情況:A 網站是一家銀行,用戶登錄以后,又去瀏覽其他網站。如果其他網站可以讀取 A 網站的 Cookie,會發生什么?很顯然,如果 Cookie 包含隱私(比如存款總額),這些信息就會泄漏。更可怕的是,Cookie 往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。因為瀏覽器同時還規定,提交表單不受同源政策的限制。
由此可見,“同源政策”是必需的,否則 Cookie 可以共享,互聯網就毫無安全可言了
隨著互聯網的發展,“同源政策”越來越嚴格。目前,如果非同源,共有三種行為受到限制
1、Cookie、LocalStorage 和 IndexedDB 無法讀取
2、DOM 無法獲得
3、AJAX 請求無效(可以發送,但瀏覽器會拒絕接受響應)
雖然這些限制是必要的,但是有時很不方便,合理的用途也受到影響
iframe
iframe
元素可以在當前網頁之中,嵌入其他網頁。每個iframe
元素形成自己的窗口,即有自己的window
對象。iframe
窗口之中的腳本,可以獲得父窗口和子窗口。但是,只有在同源的情況下,父窗口和子窗口才能通信;如果跨域,就無法拿到對方的DOM
[注意]關于iframe的詳細信息移步至此
比如,父窗口和子窗口的代碼如下所示,都處于localhost域下
<!-- 父窗口test.html--> <body> <iframe id="myIFrame" src="iframe.html"></iframe> <script> var iframe = document.getElementById("myIFrame"); iframe.onload = function(){ var doc = iframe.contentWindow.document; console.log(doc.getElementById('test').innerHTML);//'xiaohuochai' console.log(document.cookie);//'name=match' } </script> </body> <!-- 子窗口iframe.html--> <body> <div id="test">xiaohuochai</div> <script> document.cookie = 'name=match'; </script> </body>
如果iframe
窗口不是同源,如處于文件域下(file:///C:/Users/Administrator/Desktop/demo/js/test.html),就會報錯
<iframe id="myIFrame" src="iframe.html"></iframe> <script> var iframe = document.getElementById("myIFrame"); iframe.onload = function(){
//Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame. console.log(iframe.contentWindow.document); } </script>
上面命令中,父窗口想獲取子窗口的DOM,因為跨域導致報錯。
反之亦然,子窗口獲取主窗口的DOM也會報錯。
window.parent.document.body // 報錯
這種情況不僅適用于iframe
窗口,還適用于window.open
方法打開的窗口,只要跨域,父窗口與子窗口之間就無法通信
domain屬性
如果兩個窗口一級域名相同,只是二級域名不同,可以通過設置document.domain來使其通信
父窗口地址為https://static.xiaohuochai.site/test/test.html
子窗口地址為https://demo.xiaohuochai.site/test/iframe.html
代碼如下
<!-- 父窗口test.html--> <body> <iframe id="myIFrame" src="https://demo.xiaohuochai.site/test/iframe.html"></iframe> <script> var iframe = document.getElementById("myIFrame"); iframe.onload = function(){ var doc = iframe.contentWindow.document; console.log(doc.getElementById('test').innerHTML);//'xiaohuochai' console.log(document.cookie); } </script> </body> <!-- 子窗口iframe.html--> <body> <div id="test">xiaohuochai</div> <script> document.cookie = 'name=match'; </script> </body>
由結果所示,通過設置document.domain只能獲取DOM,而Cookie、LocalStorage 和 IndexedDB 無法讀取
錨點值
錨點值,又稱為片段標識符(fragment identifier),指的是URL的#
號后面的部分,比如http://example.com/x.html#fragment
的#fragment
。如果只是改變片段標識符,頁面不會重新刷新
父窗口可以把信息,寫入子窗口的錨點值
var src = originURL + '#' + data; document.getElementById('myIFrame').src = src;
子窗口通過監聽hashchange
事件得到通知
window.onhashchange = checkMessage; function checkMessage() { var message = window.location.hash; // ... }
同樣的,子窗口也可以改變父窗口的片段標識符
parent.location.href= target + '#' + hash;
下面是具體代碼
<!-- 父窗口test.html--> <body> <iframe id="myIFrame" src="iframe.html"></iframe> <script> var iframe = document.getElementById("myIFrame"); window.onhashchange = function (e) { console.log(/.*#(.*)/g.exec(e.newURL)[1])//'xiaohuochai' } </script> </body> <!-- 子窗口iframe.html--> <body> <div id="test">xiaohuochai</div> <script> parent.location.href = 'test.html' + '#' + test.innerHTML; </script> </body>
XDM
上面兩種方法都屬于破解,HTML5為了解決這個問題,引入了一個全新的API:跨文檔通信 API
[注意]IE8-瀏覽器不支持
跨文檔消息傳送(cross-document messaging),有時候簡稱為XDM,指的是在來自不同域的頁面間傳遞消息。例如,www.wrox.com域中的頁面與位于一個內嵌框架中的p2p.wrox.com域中的頁面通信。 在XDM機制出現之前,要穩妥地實現這種通信需要花很多工夫。XDM把這種機制規范化,讓我們能既穩妥又簡單地實現跨文檔通信
XDM的核心是postMessage ()方法。在HTML5規范中,除了 XDM部分之外的其他部分也會提到這個方法名,但都是為了同一個目的:向另一個地方傳遞數據。對于XDM而言,“另一個地方”指的是包含在當前頁面中的<iframe>元素,或者由當前頁面彈出的窗口
postMessage()方法接收兩個參數:一條消息和一個表示消息接收方來自哪個域的字符串。第二個參數對保障安全通信非常重要,可以防止瀏覽器把消息發送到不安全的地方
來看下面的例子。
//注意:所有支持XDM的瀏覽器也支持ifraaie的contentWindow屬性 var iframeWindow = document.getElementById("rayframe").contentWindow, iframeWindow.postMessage( "A secret', "http://www.wrox.com");
最后一行代碼嘗試向內嵌框架中發送一條消息,并指定框架中的文檔必須來源于"http://www.wrox.com"域。如果來源匹配,消息會傳遞到內嵌框架中;否則,postMessage()什么也不做。 這一限制可以避免窗口中的位置在你不知情的情況下發生改變。如果傳給postMessage()的第二個參數是"*",則表示可以把消息發送給來自任何域的文檔,但不推薦這樣做
接收到XDM消息時,會觸發window對象的message事件。這個事件是以異步形式觸發的,因此從發送消息到接收消息(觸發接收窗口的message事件)可能要經過一段時間的延遲。觸發message事件后,傳遞給onmessage處理程序的事件對象包含以下三方面的重要信息
data:作為postMessage()第一個參數傳入的字符串數據 origin:發送消息的文檔所在的域,例如"http://www.wrox.com"。 source:發送消息的文檔的window對象的代理。這個代理對象主要用于在發送上一條消息的窗口中調用postMessage()方法。如果發送消息的窗口來自同一個域,那這個對象就是window
接收到消息后驗證發送窗口的來源是至關重要的。就像給postMessage()方法指定第二個參數, 以確保瀏覽器不會把消息發送給未知頁面一樣,在onmessage處理程序中檢測消息來源可以確保傳入的消息來自已知的頁面。基本的檢測模式如下
window.onmessage = function(e){ if(e.origin == 'http://www.wrox.com'){ //處理接收到的數據 processMessage(e.data); //可選:向來源窗口發送回執 e.source.postMessage("Received!", "http//p2p.wrox.com"); } }
[注意]event.source大多數情況下只是window對象的代理,并非實際的window對象。換句話說,不能通過這個代理對象訪問window對象的其他任何信息。只通過這個代理調用 postMessage()就好,這個方法永遠存在,永遠可以調用
XDM還有一些怪異之處
postMessage()的第一個參數最早是作為“永遠都是字符串”來實現的。但后來這個參數的定義改了,改成允許傳入任何數據結構。可是,并非所有瀏覽器都實現了這一變化。為保險起見,使用postMessage()時,最好還是只傳字符串。如果想傳入結構化的數據,最佳選擇是先在要傳入的數據上調用JSON.stringify(),通過postMessage()傳入得到的字符串,然 后再在onmessage事件處理程序中調用JSON.parse()
在通過內嵌框架加載其他域的內容時,使用XDM是非常方便的。因此,在混搭(mashup)和社交網絡應用中,這種傳遞消息的方法極為常用。有了XDM,包含<iframe>的頁面可以確保自身不受惡意內容的侵擾,因為它只通過XDM與嵌入的框架通信。而XDM也可以在來自相同域的頁面間使用
下面是一個實例
父窗口地址為https://static.xiaohuochai.site/test/test_1.html
子窗口地址為https://demo.xiaohuochai.site/test/iframe_1.html
代碼如下
<!-- 父窗口test_1.html--> <body> <iframe id="myIFrame" src="https://demo.xiaohuochai.site/test/iframe_1.html"></iframe> <script> window.onmessage = function (e) { if(e.origin === 'https://demo.xiaohuochai.site'){ console.log(e.data);//'xiaohuochai' } } </script> </body> <!-- 子窗口iframe_1.html--> <body> <script> if (window.parent !== window.self) { window.parent.postMessage('xiaohuochai', 'https://static.xiaohuochai.site'); } </script> </body>
文章列表