概述
JavaScript出于安全方面的考慮两嘴,不允許跨域調(diào)用其他頁面的對象肠套。但在安全限制的同時也給注入iframe或是ajax應(yīng)用上帶來了不少麻煩栗柒。這里把涉及到跨域的一些問題簡單地整理一下:
首先什么是跨域,簡單地理解就是因為JavaScript同源策略的限制呀伙,http://a.com?域名下的js無法操作http://b.com或是http://c.a.com域名下的對象补履。更詳細的說明可以看下表:
對于主域相同子域不同的通信方法這里不一一列舉了,這里主要講解一下跨主域的通信問題区匠。
postMessage方法
window.postMessage 是一個用于安全的使用跨源通信的方法干像。通常,不同頁面上的腳本只在這種情況下被允許互相訪問驰弄,當且僅當執(zhí)行它們的頁面所處的位置使用相同的協(xié)議(通常都是 http)麻汰、相同的端口(http默認使用80端口)和相同的主機(兩個頁面的 document.domain 的值相同)。 在正確使用的情況下戚篙,window.postMessage 提供了一個受控的機制來安全地繞過這一限制五鲫。
兼容性
http://caniuse.com/#search=postMessage
具體用法
發(fā)送消息:destination.postMessage(message, targetOrigin);
destination: 目標窗口
message:發(fā)送的消息
targetOrigin: 定義發(fā)送消息的范圍
監(jiān)聽接受消息:window.addEventListener(‘message’,callback,false);
已知問題
部分版本IE8/9瀏覽器只支持iframe通信,不支持tabs之間通信岔擂。
IE8/9不能傳輸對象位喂,只能傳輸string。
參考資料
mozlia官方文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Window.postMessage
IE官方文檔:https://status.modern.ie/postmessage
webplatform文檔:https://docs.webplatform.org/wiki/apis/web-messaging/MessagePort/postMessage
HTML5官方文檔:https://html.spec.whatwg.org/multipage/comms.html#web-messaging
window.navigator(適用于ie6/7)
ie6/7有個漏洞,父窗口與所有的iframe共享window.navigator對象乱灵,可以利用這個漏洞塑崖,由于ie6/7不支持postMessage,所以可以利用這個漏洞對ie6/7做兼容跨域通信支持。
具體用法
途中A過程和B過程都是初始化監(jiān)聽事件痛倚,類似于onmessage事件规婆。只不過實現(xiàn)方法不一樣而已。
按執(zhí)行順序來描述的話,如下:
B: 父窗口向window.navigator添加一個監(jiān)聽函數(shù)抒蚜,并打上父窗口的戳掘鄙。
A: 子窗口向window.navigator添加一個監(jiān)聽函數(shù),并打上子窗口的戳嗡髓。
C: 父窗口執(zhí)行post的時候操漠,調(diào)用原先子窗口添加在navigator里的監(jiān)聽函數(shù),并將要傳輸?shù)臄?shù)據(jù)作為函數(shù)參數(shù)傳入饿这。
D: 子窗口執(zhí)行post的時候浊伙,調(diào)用原先父窗口添加在navigator里的監(jiān)聽函數(shù),并將要傳輸?shù)臄?shù)據(jù)作為函數(shù)參數(shù)傳入蛹稍。
注意:子窗口和父窗口要找到對方的監(jiān)聽函數(shù)必須得事先知道對方在添加監(jiān)聽函數(shù)的時候打上的戳
JavaScript由于同源策略的限制,跨域通信一直是棘手的問題吧黄。當然解決方案也有很多:
document.domain+iframe的設(shè)置,應(yīng)用于主域相同而子域不同唆姐;
利用iframe和location.hash,數(shù)據(jù)直接暴露在了url中廓八,數(shù)據(jù)容量和類型都有限
Flash LocalConnection奉芦, 對象可在一個 SWF 文件中或多個 SWF 文件間進行通信, 只要在同一客戶端就行剧蹂,跨應(yīng)用程序声功, 可以跨域。
window.name 保存數(shù)據(jù)以及跨域 iframe 靜態(tài)代理動態(tài)傳輸方案宠叼,充分的運用了window.name因為頁面的url改變而name不改變的特性先巴。
各種方案網(wǎng)上都有很多實例代碼,大家可以自己搜索一下冒冬。
html5中最炫酷的API之一:就是?跨文檔消息傳輸Cross Document Messaging伸蚯。高級瀏覽器Internet Explorer 8+, chrome,F(xiàn)irefox , Opera 和 Safari 都將支持這個功能简烤。這個功能實現(xiàn)也非常簡單主要包括接受信息的”message”事件和發(fā)送消息的”postMessage”方法剂邮。
發(fā)送消息的”postMessage”方法
向外界窗口發(fā)送消息:
otherWindow.postMessage(message,targetOrigin);
otherWindow: 指目標窗口,也就是給哪個window發(fā)消息横侦,是 window.frames 屬性的成員或者由 window.open 方法創(chuàng)建的窗口
參數(shù)說明:
message: 是要發(fā)送的消息挥萌,類型為 String、Object (IE8枉侧、9 不支持)
targetOrigin: 是限定消息接收范圍引瀑,不限制請使用 ‘*’
接受信息的”message”事件
var onmessage = function (event) {
var data = event.data;
var origin = event.origin;
//do someing
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//for ie
window.attachEvent('onmessage', onmessage);
}
回調(diào)函數(shù)第一個參數(shù)接收 Event 對象,有三個常用屬性:
data: 消息
origin: 消息來源地址
source: 源 DOMWindow 對象
看一個簡單的來自網(wǎng)上的demo:?http://www.css88.com/demo/postmessage/
當然postmessage也有一些不足的地方:
ie8榨馁,ie9下傳遞的數(shù)據(jù)類型值支持字符串類型憨栽,不過可以使用用?JSON對象和字符串之間的相互轉(zhuǎn)換?來解決這個問題;
ie6,ie7需要寫兼容方案徒像,個人認為window.name比較靠譜黍特;
參考網(wǎng)址:
http://www.planeart.cn/?post_type=post&p=1620
數(shù)據(jù)發(fā)送端
首先我們要做的是創(chuàng)建通信發(fā)起端,也就是數(shù)據(jù)源”source”锯蛀。作為發(fā)起端灭衷,我們可以open一個新窗口,或創(chuàng)建一個iframe旁涤,往新窗口里發(fā)送數(shù)據(jù)翔曲,簡單起見,我們每6秒鐘發(fā)送一次劈愚,然后創(chuàng)建消息監(jiān)聽器瞳遍,從目標窗口監(jiān)聽它反饋的信息。
//彈出一個新窗口 var domain = 'http://scriptandstyle.com'; var myPopup = window.open(domain + '/windowPostMessageListener.html','myWindow'); //周期性的發(fā)送消息 setInterval(function(){ var message = 'Hello! The time is: ' + (new Date().getTime()); console.log('blog.local: sending message: ' + message); //send the message and target URI myPopup.postMessage(message,domain); },6000); //監(jiān)聽消息反饋 window.addEventListener('message',function(event) { if(event.origin !== 'http://scriptandstyle.com') return; console.log('received response: ',event.data); },false);
這里我使用了window.addEventListener菌羽,但在IE里這樣是不行的掠械,因為IE使用window.attachEvent。如果你不想判斷瀏覽器的類型注祖,可以使用一些工具庫猾蒂,比如jQuery或Dojo。
假設(shè)你的窗口正常的彈出來了是晨,我們發(fā)送一條消息——需要指定URI(必要的話需要指定協(xié)議肚菠、主機、端口號等)罩缴,消息接收方必須在這個指定的URI上蚊逢。如果目標窗口被替換了,消息將不會發(fā)出箫章。
我們同時創(chuàng)建了一個事件監(jiān)聽器來接收反饋信息烙荷。有一點極其重要,你一定要驗證消息的來源的URI炉抒!只有在目標方合法的情況才你才能處理它發(fā)來的消息奢讨。
如果是使用iframe,代碼應(yīng)該這樣寫:
//捕獲iframe var domain = 'http://scriptandstyle.com'; var iframe = document.getElementById('myIFrame').contentWindow; //發(fā)送消息 setInterval(function(){ var message = 'Hello! The time is: ' + (new Date().getTime()); console.log('blog.local: sending message: ' + message); //send the message and target URI iframe.postMessage(message,domain); },6000);
確保你使用的是iframe的contentWindow屬性焰薄,而不是節(jié)點對象拿诸。
數(shù)據(jù)接收端
下面我們要開發(fā)的是數(shù)據(jù)接收端的頁面。接收方窗口里有一個事件監(jiān)聽器塞茅,監(jiān)聽“message”事件亩码,一樣,你也需要驗證消息來源方的地址野瘦。消息可以來自任何地址描沟,要確保處理的消息是來自一個可信的地址飒泻。
//響應(yīng)事件 window.addEventListener('message',function(event) { if(event.origin !== 'http://davidwalsh.name') return; console.log('message received: ' + event.data,event); event.source.postMessage('holla back youngin!',event.origin); },false);
上面的代碼片段是往消息源反饋信息,確認消息已經(jīng)收到吏廉。下面是幾個比較重要的事件屬性:
source – 消息源泞遗,消息的發(fā)送窗口/iframe。
origin – 消息源的URI(可能包含協(xié)議席覆、域名和端口)史辙,用來驗證數(shù)據(jù)源。
data – 發(fā)送方發(fā)送給接收方的數(shù)據(jù)佩伤。
這三個屬性是消息傳輸中必須用到的數(shù)據(jù)聊倔。
使用window.postMessage
跟其他很web技術(shù)一樣,如果你不校驗數(shù)據(jù)源的合法性生巡,那使用這種技術(shù)將會變得很危險耙蔑;你的應(yīng)用的安全需要你對它負責。window.postMessage就像是PHP相對于JavaScript技術(shù)孤荣。window.postMessage很酷甸陌,不是嗎?