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