浏览器的同源策略与跨域
本文所有案例在本地址都可找到:https://github.com/dancingZhou/sameOrigin/tree/dev
什么是同源策略
兩個頁面地址中的協(xié)議、域名和端口號一致,則表示同源。
例如該地址 https://www.google.com 和以下地址對比
| http://www.google.com | 否 | 協(xié)議不一致 |
| https://google.com | 否 | 域名不一致 |
| https://www.google.com:81 | 否 | 端口號不一致 |
| https://www.google.com/a/s.html | 是 | 協(xié)議,域名和端口號都一致 |
同源策略的限制:
存儲在瀏覽器中的數(shù)據(jù),如localStroage、Cooke和IndexedDB不能通過腳本跨域訪問
不能通過腳本操作不同域下的DOM
不能通過ajax請求不同域的數(shù)據(jù)
為什么要同源策略
設(shè)置同源限制主要是為了安全,如果沒有同源限制存在瀏覽器中的Cookie等其他數(shù)據(jù)可以任意讀取,不同域下DOM任意操作,Ajax任意請求的話如果瀏覽了惡意網(wǎng)站那么就會泄漏這些隱私數(shù)據(jù)。
Cookie一般用來保存登錄狀態(tài)。在登錄一個銀行網(wǎng)站后此時瀏覽器中就保存了登錄的狀態(tài),同時瀏覽了惡意網(wǎng)站,這時Cookie的信息沒有同源限制的話惡意網(wǎng)站就可以獲取這些Cookie信息來達(dá)到不為人知的目的。
如果可以操作不同域下的DOM可以用如下方式完成盜取信息。在自己的網(wǎng)站上嵌入一個iframe地址設(shè)置成銀行地址,然后讓iframe全屏顯示,當(dāng)你一不小心上當(dāng)了輸入你賬號密碼,我就可以通過DOM操作獲取到輸入的信息。
Ajax的限制同Cookie,如果帶上Cookie去跨域訪問接口就可以通過程序的驗證被認(rèn)為身份是合法的。
既然瞥見危害一角自然要嚴(yán)加防范,限制非同源操作。
怎么規(guī)避同源策略
在看法一個網(wǎng)站的過程中有的數(shù)據(jù)并不在同一臺服務(wù)器上這時怎么跨域調(diào)用就是一個很棘手的問題,可以通過以下幾個方式來規(guī)避同源的限制。
DOM同源策略的規(guī)避
hash
因為hash的改變并不會引起頁面的刷新同時可以通過 window.onhashchange事件監(jiān)聽到hash的改變,所以可以通過hash來跨域傳遞數(shù)據(jù)。
<!-- http://example.com/index.html --> <html><head><meta charset="utf8"/><title>跨域DEMO --- hash</title></head><body><iframe id="iframe" src="http://example2.com/index.html"></iframe><script>var ifra = document.getElementById('iframe');ifra.onload = function(){// ifra 加載完成了ifra.src = ifra.src + '#data';}</script></body> </html> <!-- http://example2.com/index.html --> <!-- 在iframe中的頁面(example2.com)如果和iframe所在頁面(example.com)不同域是不能獲取所在頁面的DOM,然后通過hash將數(shù)據(jù)傳遞回去的,也就是說如果同域就可以通過該方法向所在頁面?zhèn)鬟f數(shù)據(jù) --> <html><head><meta charset="utf8"/><title>跨域DEMO --- hash</title></head><body><script>window.onhashchange = function(){// 打印通過hash傳過來的數(shù)據(jù)console.log( location.hash ); }</script></body> </html>該方法會直接暴露所傳遞的數(shù)據(jù)并且對所傳數(shù)據(jù)有大小限制。
document.domain
若兩個文檔的域相同則可以獲取對方的DOM對象,并且可以通過設(shè)置 document.domain 的值來讓兩個文檔的域保持一致,但是 document.domain 并不是可以設(shè)置任何值,只能設(shè)置為當(dāng)前域的超域,比如:
m.example.com 設(shè)置為 example.com,并且不能 example.com 設(shè)置為 m.example.com 也不能將 m.example.com 設(shè)置為 example2.com。
所以document.domain只可以在擁有相同的主域名的不同子域名之間跨域。
<!--http://a.example.com--> <html><head><meta charset="utf8"/><title>跨域DEMO --- document.domain</title></head><body><p id="data">我在 a.example.com 下</p><iframe id="ifra" src="http://b.example.com/index.html"></iframe><script>// 這里為什么也要設(shè)置呢?因為document.domain的賦值會導(dǎo)致端口被覆蓋成null,并且js中沒有手段單獨(dú)設(shè)置端口,所以這里設(shè)置一遍這樣就和iframe中的一致了。document.domain = 'example.com';var ifra = document.getElementById('ifra');ifra.onload = function(){console.log( ifra.contentWindow.document.getElementById('data').innerHTML ); // 我是b.example.com下的}</script></body> </html> <!--http://b.example.com--> <html><head><meta charset="utf8"/><title>跨域DEMO --- document.domain</title></head><body><p id="data">我是b.example.com下的</p><script>document.domain = 'example.com';console.log( parent.document.getElementById('data').innerHTML ); // 我在 a.example.com 下</script></body> </html>window.name
window.name有一個特性,即使當(dāng)前窗口的地址改變了window.name的值也不會改變。可以利用這一特性來進(jìn)行跨域,步驟如下:
window.postMessage
以上幾種跨域的方法都屬于破解行為,而postMessage是H5為跨域提供的解決方法。
otherWindow.postMessage(message, targetOrigin[, transfer]) <!--http://example.com--> <html><head><meta charset="utf8"/><title>跨域DEMO --- window.postMessage</title></head><body><iframe id="ifra" src="http://example3.com/index.html"/><script>var ifra = document.getElementById('ifra');ifra.onload = function(){ifra.contentWindow.postMessage('我來自example.com', 'http://example3.com')}</script></body> </html> <!--http://example3.com--> <html><head><meta charset="utf8"/><title>跨域DEMO --- window.postMessage</title></head><body><iframe src="http://example2.com/index.html"/><script>window.addEventListener('message', function(messageEvent){console.log( messageEvent.data ); // 我來自example.com}, false)</script></body> </html>messageEvent對象上的屬性中有三個屬性要注意,分別是:
Ajax同源策略的規(guī)避
jsonp
雖然跨域限制了Ajax請求,但是卻并不影響跨域引用腳本。
<script>function callback (data) {console.log(data); // 上面的加載完成之后就會打印出后臺傳過來的數(shù)據(jù) "數(shù)據(jù)"} </script> <script src="http://example.com/index.php?arg=val1&jsonp=callback"></script> <?phpecho $_GET['jsonp'] . '(' . '數(shù)據(jù)' . ')'; ?>上面的 index.php 接口返回的是一段調(diào)用 函數(shù)的js代碼 callback(data),其中data就是接口要返回的數(shù)據(jù)。而這個callback是事先在頁面上寫好的處理數(shù)據(jù)的回調(diào)函數(shù),并且回調(diào)函數(shù)的名稱通過URL參數(shù)的形式傳遞給了后端接口,這樣就完成了一次跨域。
注:jsonp只支持GET請求。
CORS
cors(跨域資源共享)
參考
總結(jié)
以上是生活随笔為你收集整理的浏览器的同源策略与跨域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS中捉摸不透的==(宽松等于)
- 下一篇: HTML5与CSS3权威指南笔记案例1