瀏覽器同源策略
瀏覽器出于安全考慮,默認情況下,只允許在本域接口下進行數據交互。這是瀏覽器的一種安全保護機制。主要限制有兩個方面:
一、不同源的文檔不能通過ajax實現請求,例如通過XHR實現Ajax通信,默認,XHR只能訪問與包含它的頁面位于同一個域中的資源,而不能訪問其他域下的資源。
二、瀏覽器中不同域的框架也是不能通過js進行交互操作。
什么是跨域?
只要協議、域名、端口號中有任何一個不相同,就算是不同的域。要實現在不同域之間進行數據交互,就需要用到跨域的技術。
對于端口號和協議不同的情況,只能通過后臺實現跨域。
跨域的幾種方式:
1、CORS(跨域資源共享Cross-Origin Resource Sharing)
定義了在必須跨域訪問的情況下,服務器和瀏覽器是如何進行溝通。cors背后的思想就是通過自定義的HTP頭部的信息的反饋,決定整個請求響應過程的成敗。
比較一下在使用cors實現跨域和沒有跨域的情況下,ajax代碼的區別:
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 實現跨域 ? ?URL換成其他域的絕對地址
xhr.send();
如上使用get方法發送一個請求時,在他的頭部附加一個Origin頭部,其中包含請求頁面的源信息(協議、域名、端口)
Origin: http://baidu.com:8080
服務器根據這個頭部信息來決定是否允許響應,如果服務器認為這個請求可以接受,就會在Access-Control-Allow-Origin的頭部中回發同樣的源信息
Access-Control-Allow-Origin: http://baidu.com:8080
如果沒有這個頭部,或者源信息不匹配,瀏覽器就會駁回請求。
2、JSONP
(JSON with padding)是JSON的一種使用格式,可以讓網頁訪問其他網域下的資源,也叫填充式JSON
JSONP的特性:
html中的script標簽可以引用其他域中的文件,可實現跨域訪問。需要后端的支持與配合。
JSONP由兩部分組成:回調函數和數據; ??
回調函數就是在響應到來的時候,在頁面中調用的函數,數據就是服務器發過來的json數據
callback({"name":"sty"})
使用jsonp來實現跨域:
js文件
var script=document.createElement('script'); ?//創建script元素
script.src='http://a.jrg.com:8080/getNews?callback=appendHtml'; ?//指定要訪問的URL,其中回調函數是appendChild,用于處理數據
document.head.appendChild(script); ?//在頁面頭部添加script節點
document.head.removeChild(script); ?//在實現跨域功能后,移除script節點,保持頁面的簡潔
后端:
var cb = req.query.callback;
if(cb) {
? ? ?res.send(cb+'('+JSON.stringify(data)+')');
} else {
res.send(data);
}
JSONP的優缺點
JSONP的優點是:它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;并且在請求完畢后可以通過調用callback的方式回傳結果。
JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。
CORS和JSONP對比
1、 JSONP只能實現GET請求,而CORS支持所有類型的HTTP請求。
2、 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。
3、 JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS)。
使用JSONP實現跨域,也會存在一定的安全隱患,例如XSS攻擊
3、降域
瀏覽器中不同域的框架也是不能通過js進行交互的,但是不同框架之間可以獲取到window對象,但卻無法獲取到相應的屬性和方法。
例如? a.baidu.com 域下的一個 html 文檔里有一個在其他域下(b.baidu.com)的 iframe 框架,在a.baidu.com 中并不能訪問到 b.baidu.com 里的數據;
但可以獲取到 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只適用于不同子域的框架之間實現跨域訪問。
主要使用document.domain()
4、postMessage
這是HTML5的一種跨域訪問資源的方法。
window.postMessage(message,targetOrigin)方法是html5新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持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上綁定一個監聽事件,監測message*/
document.querySelector('.main input').value=e.data//通過e.data可以監聽到別人發給我的信息
console.log(e.data);
})