瀏覽器同源策略
瀏覽器出于安全考慮,默認(rèn)情況下囤萤,只允許在本域接口下進(jìn)行數(shù)據(jù)交互。這是瀏覽器的一種安全保護(hù)機制是趴。主要限制有兩個方面:
一涛舍、不同源的文檔不能通過ajax實現(xiàn)請求,例如通過XHR實現(xiàn)Ajax通信唆途,默認(rèn)富雅,XHR只能訪問與包含它的頁面位于同一個域中的資源掸驱,而不能訪問其他域下的資源。
二没佑、瀏覽器中不同域的框架也是不能通過js進(jìn)行交互操作毕贼。
什么是跨域?
只要協(xié)議图筹、域名、端口號中有任何一個不相同让腹,就算是不同的域远剩。要實現(xiàn)在不同域之間進(jìn)行數(shù)據(jù)交互,就需要用到跨域的技術(shù)骇窍。
對于端口號和協(xié)議不同的情況瓜晤,只能通過后臺實現(xiàn)跨域。
跨域的幾種方式:
1腹纳、CORS(跨域資源共享Cross-Origin Resource Sharing)
定義了在必須跨域訪問的情況下痢掠,服務(wù)器和瀏覽器是如何進(jìn)行溝通。cors背后的思想就是通過自定義的HTP頭部的信息的反饋嘲恍,決定整個請求響應(yīng)過程的成敗足画。
比較一下在使用cors實現(xiàn)跨域和沒有跨域的情況下,ajax代碼的區(qū)別:
var xhr=new XMLHttpRequest();
xhr.open('get','/loadMore?index='+pageIndex+'&length=5',true) ?// ?沒有cors ?url是一個相對路徑
xhr.send();
var ?xhr=new ?XMLHttpRequest();
xhr.open('get', 'http://baidu.com:8080/getNews', true); // cors 實現(xiàn)跨域 ? ?URL換成其他域的絕對地址
xhr.send();
如上使用get方法發(fā)送一個請求時佃牛,在他的頭部附加一個Origin頭部淹辞,其中包含請求頁面的源信息(協(xié)議、域名俘侠、端口)
Origin: http://baidu.com:8080
服務(wù)器根據(jù)這個頭部信息來決定是否允許響應(yīng)象缀,如果服務(wù)器認(rèn)為這個請求可以接受,就會在Access-Control-Allow-Origin的頭部中回發(fā)同樣的源信息
Access-Control-Allow-Origin: http://baidu.com:8080
如果沒有這個頭部爷速,或者源信息不匹配央星,瀏覽器就會駁回請求。
2惫东、JSONP
(JSON with padding)是JSON的一種使用格式莉给,可以讓網(wǎng)頁訪問其他網(wǎng)域下的資源,也叫填充式JSON
JSONP的特性:
html中的script標(biāo)簽可以引用其他域中的文件廉沮,可實現(xiàn)跨域訪問禁谦。需要后端的支持與配合。
JSONP由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù); ??
回調(diào)函數(shù)就是在響應(yīng)到來的時候废封,在頁面中調(diào)用的函數(shù)州泊,數(shù)據(jù)就是服務(wù)器發(fā)過來的json數(shù)據(jù)
callback({"name":"sty"})
使用jsonp來實現(xiàn)跨域:
js文件
var script=document.createElement('script'); ?//創(chuàng)建script元素
script.src='http://a.jrg.com:8080/getNews?callback=appendHtml'; ?//指定要訪問的URL,其中回調(diào)函數(shù)是appendChild,用于處理數(shù)據(jù)
document.head.appendChild(script); ?//在頁面頭部添加script節(jié)點
document.head.removeChild(script); ?//在實現(xiàn)跨域功能后漂洋,移除script節(jié)點遥皂,保持頁面的簡潔
后端:
var cb = req.query.callback;
if(cb) {
? ? ?res.send(cb+'('+JSON.stringify(data)+')');
} else {
res.send(data);
}
JSONP的優(yōu)缺點
JSONP的優(yōu)點是:它不像XMLHttpRequest對象實現(xiàn)的Ajax請求那樣受到同源策略的限制力喷;它的兼容性更好,在更加古老的瀏覽器中都可以運行演训,不需要XMLHttpRequest或ActiveX的支持弟孟;并且在請求完畢后可以通過調(diào)用callback的方式回傳結(jié)果。
JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求样悟;它只支持跨域HTTP請求這種情況拂募,不能解決不同域的兩個頁面之間如何進(jìn)行JavaScript調(diào)用的問題。
CORS和JSONP對比
1窟她、 JSONP只能實現(xiàn)GET請求陈症,而CORS支持所有類型的HTTP請求。
2震糖、 使用CORS录肯,開發(fā)者可以使用普通的XMLHttpRequest發(fā)起請求和獲得數(shù)據(jù),比起JSONP有更好的錯誤處理吊说。
3论咏、 JSONP主要被老的瀏覽器支持,它們往往不支持CORS颁井,而絕大多數(shù)現(xiàn)代瀏覽器都已經(jīng)支持了CORS)厅贪。
使用JSONP實現(xiàn)跨域,也會存在一定的安全隱患雅宾,例如XSS攻擊
3卦溢、降域
瀏覽器中不同域的框架也是不能通過js進(jìn)行交互的,但是不同框架之間可以獲取到window對象秀又,但卻無法獲取到相應(yīng)的屬性和方法单寂。
例如? a.baidu.com 域下的一個 html 文檔里有一個在其他域下(b.baidu.com)的 iframe 框架,在a.baidu.com 中并不能訪問到 b.baidu.com 里的數(shù)據(jù)吐辙;
但可以獲取到 b.baidu.com 中的 window 對象宣决,但是這時 window 的屬性和方法并不可用,兩個文件中使用?document.domain('baidu.com')方法?把域名都降到baidu.com昏苏;
這樣就可以在 a.baidu.com 中使用 iframe 里面的 window 的所有屬性和方法了尊沸,通過window.frames[0] 獲取到 iframe 框架,但是這時再通過window.frames[0].document.querySelector(element) 獲取到 iframe 里的 element 元素贤惯。
在b.baidu.com 中通過window.parent.document.querySelector(element) 獲取到html里的元素洼专。
document.domain只適用于不同子域的框架之間實現(xiàn)跨域訪問。
主要使用document.domain()
4孵构、postMessage
這是HTML5的一種跨域訪問資源的方法屁商。
window.postMessage(message,targetOrigin)方法是html5新引進(jìn)的特性,可以使用它來向其它的window對象發(fā)送消息颈墅,無論這個window對象是屬于同源或不同源蜡镶,目前IE8+雾袱、FireFox、Chrome官还、Opera等瀏覽器都已經(jīng)支持window.postMessage方法芹橡。
document.querySelector('.main input').addEventListener('input',function() {
console.log(this.value);
window.frames[0].postMessage(this.value,"*") ?//訪問不同源的框架的message
})
window.addEventListener('message',function(e) { ?/*在window上綁定一個監(jiān)聽事件,監(jiān)測message*/
document.querySelector('.main input').value=e.data//通過e.data可以監(jiān)聽到別人發(fā)給我的信息
console.log(e.data);
})