備注:補充一個額外方案:建立一個中間頁面,使子視窗跳轉到和主視窗同域的頁面中,再使用子視窗的parent操作父視窗,
跨視窗通訊
“相同來源”(相同站點)策略限制了視窗和框架之間的訪問,
這樣的想法是,如果用戶打開了兩個頁面:一個來自john-smith.com,而另一個打開gmail.com,則他們將不需要腳本來john-smith.com從中讀取我們的郵件gmail.com,因此,“相同來源”策略的目的是保護用戶免遭資訊盜竊,
同源
如果兩個URL具有相同的協議,域和埠,則稱它們具有“相同的來源”,
這些URL都具有相同的來源:
http://site.comhttp://site.com/http://site.com/my/page.html
這些沒有:
http://www.site.com(另一個領域:www.重要)http://site.org(另一個領域:.org重要)https://site.com(另一種協議:https)http://site.com:8080(另一個埠:8080)
“相同來源”政策規定:
- 如果我們參考了另一個視窗,例如由創建的彈出
window.open視窗或內部的一個視窗<iframe>,并且該視窗來自同一原點,則可以完全訪問該視窗, - 否則,如果它來自另一個來源,那么我們將無法訪問該視窗的內容:變數,檔案等,唯一的例外是
location:我們可以更改它(從而重定向用戶),但是我們無法讀取位置(因此我們無法看到用戶現在所在的位置,也不會泄漏任何資訊),
實際應用中:iframe
一個<iframe>標簽主機單獨的嵌入式視窗,有自己的獨立document和window物件,
我們可以使用屬性訪問它們:
iframe.contentWindow使窗戶進入窗戶<iframe>,iframe.contentDocument將檔案放入的<iframe>簡寫iframe.contentWindow.document,
當我們訪問嵌入式視窗內的內容時,瀏覽器會檢查iframe的來源是否相同,如果不是這樣,則訪問被拒絕(寫入location是一個例外,它仍然被允許),
例如,讓我們嘗試<iframe>從另一個來源進行讀寫:
<iframe src="https://example.com" id="iframe"></iframe>
<script>
iframe.onload = function() {
// we can get the reference to the inner window
let iframeWindow = iframe.contentWindow; // OK
try {
// ...but not to the document inside it
let doc = iframe.contentDocument; // ERROR
} catch(e) {
alert(e); // Security Error (another origin)
}
// also we can't READ the URL of the page in iframe
try {
// Can't read URL from the Location object
let href = iframe.contentWindow.location.href; // ERROR
} catch(e) {
alert(e); // Security Error
}
// ...we can WRITE into location (and thus load something else into the iframe)!
iframe.contentWindow.location = '/'; // OK
iframe.onload = null; // clear the handler, not to run it after the location change
};
</script>
上面的代碼顯示了除以下各項以外的所有操作的錯誤:
- 獲取對內部視窗的參考
iframe.contentWindow-允許的, - 寫信給
location,
與此相反,如果<iframe>起源相同,我們可以使用它做任何事情:
<!-- iframe from the same site -->
<iframe src="/" id="iframe"></iframe>
<script>
iframe.onload = function() {
// just do anything
iframe.contentDocument.body.prepend("Hello, world!");
};
</script>
iframe.onload 與 iframe.contentWindow.onload
的iframe.onload(在事件<iframe>標簽)是基本上相同iframe.contentWindow.onload(在嵌入視窗物件),當嵌入式視窗完全加載所有資源時觸發,
…但是我們無法iframe.contentWindow.onload從其他來源訪問iframe,因此請使用iframe.onload,
Windows在子域上:document.domain
根據定義,兩個具有不同域的URL具有不同的來源,
但是,如果視窗共享同一個二級域名,例如john.site.com,peter.site.com和site.com(使他們共同的二級域名site.com),我們可以讓瀏覽器忽略這種差別,使他們能夠從“同根同源”來對待為了跨視窗交流的目的,
為了使其正常作業,每個這樣的視窗都應運行以下代碼:
document.domain = 'site.com';
就這樣,現在,他們可以不受限制地進行互動,同樣,這僅適用于具有相同二級域的頁面,
iframe:錯誤的檔案陷阱
當iframe來自同一來源時,我們可能會訪問它 document,這是一個陷阱,它與跨源事物無關,但重要的是要知道,
創建后,iframe會立即擁有一個檔案,但是該檔案不同于加載到其中的檔案!
因此,如果我們立即對檔案進行操作,則可能會丟失,
在這里,看看:
<iframe src="/" id="iframe"></iframe>
<script>
let oldDoc = iframe.contentDocument;
iframe.onload = function() {
let newDoc = iframe.contentDocument;
// the loaded document is not the same as initial!
alert(oldDoc == newDoc); // false
};
</script>
我們不應該處理尚未加載的iframe的檔案,因為那是錯誤的檔案,如果我們在其上設定了任何事件處理程式,它們將被忽略,
如何檢測檔案在那里的時刻?
iframe.onload 觸發時,正確的檔案肯定存在,但是,只有在加載了所有資源的整個iframe時,它才會觸發,
我們可以嘗試使用check in來趕上這一時刻setInterval:
<iframe src="/" id="iframe"></iframe>
<script>
let oldDoc = iframe.contentDocument;
// every 100 ms check if the document is the new one
let timer = setInterval(() => {
let newDoc = iframe.contentDocument;
if (newDoc == oldDoc) return;
alert("New document is here!");
clearInterval(timer); // cancel setInterval, don't need it any more
}, 100);
</script>
集合:window.frames
獲取–的視窗物件的另一種方法<iframe>是從命名集合中獲取它 window.frames:
- 按編號:
window.frames[0]–檔案中第一幀的視窗物件, - 按名稱:
window.frames.iframeName–具有的框架的視窗物件name="iframeName",
例如:
<iframe src="/" style="height:80px" name="win" id="iframe"></iframe>
<script>
alert(iframe.contentWindow == frames[0]); // true
alert(iframe.contentWindow == frames.win); // true
</script>
一個iframe可能內部還有其他iframe,相應的window物件形成層次結構,
導航鏈接為:
window.frames–“子級”視窗的集合(用于嵌套框架),window.parent–對“父”(外部)視窗的參考,window.top–對最頂層父視窗的參考,
例如:
window.frames[0].parent === window; // true
我們可以使用該top屬性來檢查當前檔案是否在框架內打開:
if (window == top) { // current window == window.top?
alert('The script is in the topmost window, not in a frame');
} else {
alert('The script runs in a frame!');
}
“沙盒” iframe屬性
該sandbox屬性允許排除<iframe>in 內部的某些操作,以防止其執行不受信任的代碼,它通過將iframe視為來自其他來源和/或應用其他限制來對其進行“沙盒化”,
有適用于的“默認設定”限制<iframe sandbox src="https://www.cnblogs.com/lxhbky/archive/2020/09/28/...">,但是,如果我們提供一個以空格分隔的限制串列,可以將其放寬,這些限制不應用作屬性的值,例如:<iframe sandbox="allow-forms allow-popups">,
換句話說,空"sandbox"屬性會施加最嚴格的限制,但我們可以放置一個用空格分隔的串列,以列出要洗掉的屬性,
以下是限制的串列:
allow-same-origin- 默認情況下
"sandbox",對iframe強制實施“不同來源”策略,換句話說,它使瀏覽器將iframe視為來自另一個來源,即使其src指向同一站點也是如此,具有所有隱含的腳本限制,此選項將洗掉該功能, allow-top-navigation- 允許
iframe更改parent.location, allow-forms- 允許提交來自的表格
iframe, allow-scripts- 允許從運行腳本
iframe, allow-popups- 允許從
window.open彈出視窗iframe
有關更多資訊,請參見手冊,
以下示例演示了具有默認限制集的沙盒iframe <iframe sandbox src="https://www.cnblogs.com/lxhbky/archive/2020/09/28/...">,它具有一些JavaScript和一種形式,
請注意,沒有任何效果,因此,默認設定確實很苛刻:
結果 index.html sandboxed.html 請注意:該"sandbox"屬性的目的只是添加更多限制,它無法洗掉它們,特別是,如果iframe來自其他來源,則無法放寬同源限制,
跨視窗訊息傳遞
該postMessage界面允許Windows彼此交談,無論它們來自哪個來源,
因此,這是解決“相同來源”政策的一種方法,它允許從視窗john-smith.com進行對話gmail.com和交換資訊,但前提是它們都同意并呼叫相應的JavaScript函式,這對用戶來說很安全,
該界面分為兩個部分,
postMessage
想要發送訊息的視窗呼叫接收視窗的postMessage方法,換句話說,如果要將訊息發送到win,則應致電 win.postMessage(data, targetOrigin),
引數:
data- 要發送的資料,可以是任何物件,可以使用“結構化序列化演算法”克隆資料,IE僅支持字串,因此我們應該使用
JSON.stringify復雜的物件來支持該瀏覽器, targetOrigin- 指定目標視窗的原點,以便只有給定原點的視窗才能獲取訊息,
這targetOrigin是一項安全措施,請記住,如果目標視窗來自另一個來源,我們將無法location在發送方視窗中讀取它,因此,我們不能確定現在正在預期的視窗中打開了哪個站點:用戶可以導航離開,并且發件人視窗對此一無所知,
指定該選項targetOrigin可確保視窗僅在正確位置時才接收資料,當資料敏感時很重要,
例如,win僅在源頭有檔案的情況下,此處才接收訊息http://example.com:
<iframe src="http://example.com" name="example">
<script>
let win = window.frames.example;
win.postMessage("message", "http://example.com");
</script>
如果我們不想檢查,可以將其設定targetOrigin為*,
<iframe src="http://example.com" name="example">
<script>
let win = window.frames.example;
win.postMessage("message", "*");
</script>
訊息
要接收訊息,目標視窗應具有message事件處理程式,觸發何時postMessage呼叫(并targetOrigin檢查成功),
事件物件具有特殊的屬性:
data- 來自的資料
postMessage, origin- 例如,發件人的來源
http://javascript.info, source- 對發件人視窗的參考,
source.postMessage(...)如果需要,我們可以立即回傳,
要分配該處理程式,我們應該使用addEventListener,短語法window.onmessage不起作用,
這是一個例子:
window.addEventListener("message", function(event) {
if (event.origin != 'http://javascript.info') {
// something from an unknown domain, let's ignore it
return;
}
alert( "received: " + event.data );
// can message back using event.source.postMessage(...)
});
完整的例子:
結果 iframe.html index.html概要
要呼叫方法并訪問另一個視窗的內容,我們應該首先對其進行參考,
對于彈出視窗,我們有以下參考:
- 在打開器視窗中:
window.open–打開一個新視窗并回傳對該視窗的參考, - 從彈出視窗:
window.opener–是對彈出視窗中打開器視窗的參考,
對于iframe,我們可以使用以下方法訪問父/子視窗:
window.frames–嵌套視窗物件的集合,window.parent,window.top是對父視窗和頂部視窗的參考,iframe.contentWindow是<iframe>標簽內的視窗,
如果Windows共享相同的來源(主機,埠,協議),則Windows可以相互執行任何操作,
否則,只有可能的操作是:
- 更改
location另一個視窗的(只讀訪問), - 向其發布訊息,
例外是:
- 共享相同二級域的Windows:
a.site.com和b.site.com,然后document.domain='site.com'將它們都設定為“原點相同”狀態, - 如果iframe具有
sandbox屬性,則除非allow-same-origin該屬性值中指定,否則它將被強制置于“其他來源”狀態,這可用于在同一站點的iframe中運行不受信任的代碼,
該postMessage界面允許兩個具有任何起源的視窗進行交談:
-
發件人呼叫
targetWin.postMessage(data, targetOrigin), -
如果
targetOrigin不是'*',則瀏覽器檢查windowtargetWin是否具有原點targetOrigin, -
如果是這樣,則使用特殊屬性
targetWin觸發message事件:origin–發送者視窗的來源(如http://my.site.com)source–對發件人視窗的參考,data–資料,除IE僅支持字串的任何地方的任何物件,
我們應該使用
addEventListener它在目標視窗內設定此事件的處理程式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/134955.html
標籤:其他
