瀏覽器同源策略
含義
所謂同源策略,指的是瀏覽器對(duì)不同源的腳本或者文本的訪問(wèn)方式進(jìn)行的限制。比如源a的js不能讀取或設(shè)置引入的源b的元素屬性。
那么先定義下什么是同源,所謂同源,就是指兩個(gè)頁(yè)面具有相同的協(xié)議,主機(jī)(也常說(shuō)域名),端口,三個(gè)要素缺一不可。
同源是指
* 協(xié)議相同
* 域名相同
* 端口相同
目的
同源策略的目的,是為了保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)。瀏覽器同時(shí)訪問(wèn)2個(gè)跨域網(wǎng)站,如果A可以訪問(wèn)B網(wǎng)站的信息,那么就數(shù)據(jù)隨時(shí)會(huì)被別人竊取。
限制范圍
隨著互聯(lián)網(wǎng)的發(fā)展,"同源政策"越來(lái)越嚴(yán)格。目前,如果非同源,共有三種行為受到限制。
* Cookie、LocalStorage和IndexDB無(wú)法讀取。
* DOM 無(wú)法獲取。
* AJAX 請(qǐng)求不能發(fā)送。
跨域的幾種解決方法
Cookie
Cookie 是服務(wù)器寫入瀏覽器的一小段信息,只有同源的網(wǎng)頁(yè)才能共享。但是,兩個(gè)網(wǎng)頁(yè)一級(jí)域名相同,只是二級(jí)域名不同,瀏覽器允許通過(guò)設(shè)置document.domain
共享 Cookie。
舉例來(lái)說(shuō),A網(wǎng)頁(yè)是http://w1.example.com/a.html
,B網(wǎng)頁(yè)是http://w2.example.com/b.html
,那么只要設(shè)置相同的document.domain
,兩個(gè)網(wǎng)頁(yè)就可以共享Cookie。
document.domain = 'example.com';
現(xiàn)在,A網(wǎng)頁(yè)通過(guò)腳本設(shè)置一個(gè)Cookie。
document.cookie = "test1=hello";
B網(wǎng)頁(yè)就可以讀到這個(gè) Cookie。
var allCookie = document.cookie;
注意,這種方法只適用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 無(wú)法通過(guò)這種方法,規(guī)避同源政策,而要使用其他方法規(guī)避。
也可以通過(guò)后臺(tái)服務(wù)器端設(shè)置Cookie的時(shí)候,指定Cookie的所屬域名為一級(jí)域名,比如.example.com
。
Set-Cookie: key=value; domain=.example.com; path=/
iframe
通過(guò)下面2個(gè)方法,去獲取不同源的iframe里面的DOM或者iframe里面獲取外面的DOM,都會(huì)跨域
document.getElementById("myIFrame").contentWindow.document
window.parent.document.body
解決方法和Cookie一樣,如果兩個(gè)窗口一級(jí)域名相同,只是二級(jí)域名不同,那么設(shè)置上一節(jié)介紹的document.domain
屬性,就可以規(guī)避同源政策,拿到DOM。
這3種方法在這里就不過(guò)多描敘了,就是通過(guò)url的hash值和iframe里面通信
* 片段識(shí)別符(fragment identifier)
* window.name
* 跨文檔通信API(Cross-document messaging)
JSONP
它的基本思想是,網(wǎng)頁(yè)通過(guò)添加一個(gè)<script>
元素,向服務(wù)器請(qǐng)求JSON數(shù)據(jù),這種做法不受同源政策限制;服務(wù)器收到請(qǐng)求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)里傳回來(lái)。
首先,網(wǎng)頁(yè)動(dòng)態(tài)插入<script>
元素,由它向跨源網(wǎng)址發(fā)出請(qǐng)求。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
上面代碼通過(guò)動(dòng)態(tài)添加<script>
元素,向服務(wù)器example.com
發(fā)出請(qǐng)求。注意,該請(qǐng)求的查詢字符串有一個(gè)callback
參數(shù),用來(lái)指定回調(diào)函數(shù)的名字,這對(duì)于JSOPNP是必需的。
服務(wù)器收到這個(gè)請(qǐng)求以后,會(huì)將數(shù)據(jù)放在回調(diào)函數(shù)的參數(shù)位置返回。
foo({
"ip": "8.8.8.8"
});
由于<script>
元素請(qǐng)求的腳本,直接作為代碼運(yùn)行。這時(shí),只要瀏覽器定義了foo
函數(shù),該函數(shù)就會(huì)立即調(diào)用。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對(duì)象,而不是字符串,因此避免了使用JSON.parse
的步驟。
WebSocket
WebSocket是一種通信協(xié)議,使用ws://
(非加密)和wss://
(加密)作為協(xié)議前綴。該協(xié)議不實(shí)行同源政策,只要服務(wù)器支持,就可以通過(guò)它進(jìn)行跨源通信。