- 同源政策由網(wǎng)景公司引入瀏覽器虹蒋,目前所有瀏覽器都實(shí)行這個(gè)政策飒货。
- 同源指“三個(gè)相同”:
- 協(xié)議相同
- 域名相同
- 端口相同
- 非同源有三種行為受限制:
- Cookie魄衅、LocalStorage和IndexDB無法讀忍粮ā;
- DOM無法獲得
- AJAX請(qǐng)求不能發(fā)送
- 同源政策保護(hù)了用戶信息安全莫辨,防止惡意網(wǎng)絡(luò)竊取數(shù)據(jù)傲茄,是必要的沮榜,但有時(shí)也會(huì)影響合理用途,所以有時(shí)可以采取規(guī)避方法:
- 兩個(gè)網(wǎng)頁一級(jí)域名相同蟆融,只是二級(jí)域名不同草巡,瀏覽器允許通過設(shè)置
document.domain
共享Cookie型酥。
例如,A網(wǎng)頁是http://w1.example.com/a.html弥喉,B網(wǎng)頁是http://w2.example.com/b.html郁竟,設(shè)置相同
document.domain
document.domain = 'example.com';
A網(wǎng)頁通過腳本設(shè)置一個(gè)Cookie
document.cookie = "test1=hello";
B網(wǎng)頁就可以讀到這個(gè)Cookie
- 注意由境,該方法只適用于Cookie和iframe窗口蓖议,LocalStorage和IndexDB不行。
另外讥蟆,也可以指定Cookie的所屬域名為一級(jí)域名,這樣二瘸彤、三級(jí)域名不做設(shè)置都可以讀取這個(gè)Cookie:
Set-Cookie: key=value;domain=.example.com;path=/
- 對(duì)于完全不同源的網(wǎng)站修然,目前有三種方法解決跨域窗口的通信問題:
i. 片段識(shí)別符(fragment identifier)
ii. window.name
iii. 跨文檔通信API(Cross-document messaging)
2.1 片段識(shí)別符是URL#
號(hào)后面的部分质况,只改變片段識(shí)別符,頁面不會(huì)刷新拯杠。
// 父窗口把信息寫入子窗口的片段識(shí)別符
var src = originURL + '#' +data;
document.getElementById('myIFrame').src = src;
// 子窗口通過監(jiān)聽hashchange事件得到通知
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
//...
}
// 同樣子窗口也可以改變父窗口的片段識(shí)別符
parent.location.href = target + "#" +hash;
2.2 瀏覽器窗口有window.name
屬性,特點(diǎn)是無論是否同源潭陪,只要在同一個(gè)窗口雄妥,前一個(gè)網(wǎng)頁設(shè)置了該屬性依溯,后一個(gè)網(wǎng)頁就可以讀取它老厌。
// 父窗口
2.3 HTML5引入一個(gè)新的API:跨文檔通信API(Cross-document messaging)黎炉。這個(gè)API為window對(duì)象新增一個(gè)window.postMessage
方法,允許跨窗口通信慷嗜,不論這兩個(gè)窗口是否同源淀弹。
// 父窗口打開一個(gè)子窗口
var popup = window.open('http://bbb.com','title');
// 父窗口向子窗口發(fā)消息
popup.postMessage('Hello Worlad!','http://bbb.com');
postMessage
方法的第一個(gè)參數(shù)是具體的信息內(nèi)容庆械,第二個(gè)參數(shù)是接收消息的窗口的源(origin),即“協(xié)議+域名+端口”缭乘,也可以設(shè)為“*”沐序,表示不限域名堕绩,向所有窗口發(fā)送。
// 子窗口向父窗口發(fā)消息也類似
window.opener.postMessage('Hi!','http://aaa.com');
// 父窗口和子窗口都可以通過message事件監(jiān)聽對(duì)方消息
window.addEventListener('message',function(e) {
console.log(e.data)
},false)// 默認(rèn)false奴紧,false表示句柄在冒泡階段執(zhí)行特姐,true表示句柄在捕獲階段執(zhí)行绰寞。
message事件的參數(shù)是事件對(duì)象event铣口,提供三個(gè)屬性:
i. event.source:發(fā)送消息的窗口
ii. event.origin:消息發(fā)向的窗口
iii. event.data:消息內(nèi)容
- AJAX
同源政策滤钱,AJAX請(qǐng)求只能發(fā)給同源網(wǎng)址觉壶,否則報(bào)錯(cuò)递惋。
除了架設(shè)服務(wù)器代理(瀏覽器請(qǐng)求同源服務(wù)器阐污,再由后者請(qǐng)求外部服務(wù)),有三種方法規(guī)避這個(gè)限制:
i. JSONP
ii. WebSocket
iii. CORS
- JSONP
JSONP是服務(wù)器與客戶端跨源通信的常用方法争剿,簡(jiǎn)單適用痊末,老瀏覽器也支持蚕苇。
基本思想是凿叠,通過<script>向服務(wù)器請(qǐng)求JSONP數(shù)據(jù),不受同源政策限制盒件;服務(wù)器受到請(qǐng)求后蹬碧,將數(shù)據(jù)放在指定名字的回調(diào)函數(shù)傳回來炒刁。
首先,網(wǎng)頁動(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)
}
- WebSocket
WebSocket是通信協(xié)議,協(xié)議前綴有ws://(非加密)和wss://(加密)全谤,不實(shí)行同源政策肤晓,可以跨源通信认然。 - CORS
CORS(Cross-Origin Resource Sharing)补憾,跨源資源分享卷员,是W3C標(biāo)準(zhǔn),屬于跨源AJAX請(qǐng)求的根本解決方法毕骡。JSONP只能發(fā)GET請(qǐng)求削饵,CORS允許任何類型請(qǐng)求。