問答
1. 什么是同源策略
同源是指域名铸豁、協(xié)議灌曙、端口相同。
同源策略(Same-Origin Policy)是瀏覽器的一個(gè)功能节芥;
最初在刺,它的含義是指,A網(wǎng)頁(yè)設(shè)置的 Cookie头镊,B網(wǎng)頁(yè)不能打開蚣驼,從而保證用戶信息的安全性。隨著互聯(lián)網(wǎng)的發(fā)展相艇,"同源政策"越來越嚴(yán)格颖杏。目前,如果非同源坛芽,共有三種行為受到限制:
(1) Cookie留储、LocalStorage 和 IndexDB 無法讀取。
(2) DOM 無法獲得咙轩。
(3) AJAX 請(qǐng)求不能發(fā)送获讳。
換句話說:不同源的客戶端腳本在沒明確授權(quán)的情況下,不能讀寫對(duì)方的資源活喊。
2. 什么是跨域丐膝?跨域有幾種實(shí)現(xiàn)形式
簡(jiǎn)單地理解就是因?yàn)镴avaScript同源策略的限制,a.com 域名下的js無法操作b.com或是c.a.com域名下的對(duì)象钾菊。更詳細(xì)的說明可以看下表:
實(shí)現(xiàn)形式:?
1. document.domain+iframe
應(yīng)用范圍:這種辦法只能解決主域相同而子域不同帅矗,且是iframe形式的跨域;
存在問題:安全性结缚,當(dāng)一個(gè)站點(diǎn)(b.a.com)被攻擊后损晤,另一個(gè)站點(diǎn)(c.a.com)會(huì)引起安全漏洞。
2.JSONP
應(yīng)用范圍:因?yàn)槭腔趕cript標(biāo)簽红竭,所有只能進(jìn)行GET請(qǐng)求
存在問題:存在安全性問題尤勋,可被注入可執(zhí)行的js代碼(callback=alert(1)
), 對(duì)于這個(gè)問題,只能通過外界的字符串過濾來解決茵宪,如禁止callback中傳入括號(hào)最冰,使用正則去除左右括號(hào),
callback = callback.replace( /\(/g,"" )
稀火,
callback = callback.replace( /\)/g,"" )
3.CORS
給被訪問方設(shè)置Access-Control-Allow-Origin暖哨,如在php文件頭部寫入header('Access-Control-Allow-Origin:http://a.com')
,表示允許來自源http://a.com
的請(qǐng)求。這是跨域AJAX請(qǐng)求的根本解決方法凰狞。
相比JSONP只能發(fā)GET請(qǐng)求篇裁,CORS允許任何類型的請(qǐng)求沛慢。但是IE10及以下IE版本不支持。
4.HTML5 postMessage
這是HTML5的新功能达布,用postMessage支持基于web的實(shí)時(shí)消息傳遞团甲。
5.利用iframe和location.hash
這個(gè)方法比較繞,原理是利用location.hash來傳值黍聂。url中#號(hào)及其后面的內(nèi)容就是location.hash,改變hash的值頁(yè)面并不會(huì)刷新躺苦,所以可以利用hash值來進(jìn)行數(shù)據(jù)傳遞。這種方法缺點(diǎn)也很多产还,諸如數(shù)據(jù)直接暴露在了url中匹厘,數(shù)據(jù)容量和類型都有限等。
6.利用window.name
主要利用window.name值不隨url改變而改變脐区,只要當(dāng)前頁(yè)面沒被關(guān)閉愈诚,window.name的值就不會(huì)改變。
3. jsonp 的原理是什么
ajax請(qǐng)求受同源策略影響坡椒,不允許進(jìn)行跨域請(qǐng)求扰路,而script標(biāo)簽src屬性中的鏈接卻可以訪問跨域的js腳本尤溜,利用這個(gè)特性倔叼,服務(wù)端不再返回JSON格式的數(shù)據(jù),而是返回一段調(diào)用某個(gè)函數(shù)的js代碼宫莱,在src中進(jìn)行了調(diào)用丈攒,這樣實(shí)現(xiàn)了跨域。
4. CORS是什么
CORS是一個(gè)W3C標(biāo)準(zhǔn)授霸,全稱是"跨域資源共享"(Cross-origin resource sharing)巡验。它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請(qǐng)求碘耳,從而克服了AJAX只能同源使用的限制显设。
CORS需要瀏覽器和服務(wù)器同時(shí)支持。目前辛辨,所有瀏覽器都支持該功能捕捂,IE瀏覽器不能低于IE10。
整個(gè)CORS通信過程斗搞,都是瀏覽器自動(dòng)完成指攒,不需要用戶參與。對(duì)于開發(fā)者來說僻焚,CORS通信與同源的AJAX通信沒有差別允悦,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源虑啤,就會(huì)自動(dòng)添加一些附加的頭信息隙弛,有時(shí)還會(huì)多出一次附加的請(qǐng)求架馋,但用戶不會(huì)有感覺。
因此全闷,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器绩蜻。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信室埋。
練習(xí)
1. 本地搭建服務(wù)器办绝,演示同源策略
1. 本地搭建服務(wù)器(如果使用 SAE 可創(chuàng)建不同的代碼版本,這樣可通過1.xxx.sinapp.com和2.xxx.sinapp.com 訪問了)
2. 修改 本地host姚淆,通過不同域名訪問本地服務(wù)器孕蝉。比如訪問http://a.com/index.html, http://b.com/ajax.php,本質(zhì)是
3. 在 index.html 里使用 ajax 接口訪問 http://b.com/ajax.php 里的數(shù)據(jù)腌逢。
4. 查看輸出報(bào)錯(cuò)
這里以XAMPP為例演示降淮,具體步驟:
- 在XAMPP軟件的htdocs/tests/cross-origin目錄下新建兩個(gè)文件,index.html和main.js搏讶。其中佳鳖,index.html的主要內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>同源策略</title>
</head>
<body>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script>
<!-- 測(cè)試一:a.com/index引用b.com域名下的main.js文件 -->
<script src="http://b.com/tests/cross-origin/main.js"></script>
<!-- 測(cè)試二:a.com/index通過ajax請(qǐng)求b.com域名下的main.js文件 -->
<script type="text/javascript">
$.get('//b.com/tests/cross-origin/main.js',function(response){
console.log(response);
})
</script>
<!-- 測(cè)試三:a.com/index 通過ajax請(qǐng)求a.com域名下的main.js文件 -->
<script type="text/javascript">
$.get('//a.com/tests/cross-origin/main.js',function(response){
console.log(response);
})
</script>
</body>
</html>
main.js的內(nèi)容如下:
alert (1);
- 以管理員權(quán)限運(yùn)行記事本,用記事本打開路徑Windows/System32/drivers/etc下的hosts文件媒惕,添加如下兩條IP和域名的對(duì)應(yīng)信息系吩。目的是讓a.com和b.com兩個(gè)域名都指向本地機(jī)。
127.0.0.1 a.com
127.0.0.1 b.com
- 啟動(dòng)XAMPP妒蔚,瀏覽器地址欄輸入
a.com/tests/cross-origin/index.html
- 測(cè)試結(jié)果:
測(cè)試一結(jié)果:頁(yè)面 alert(1)穿挨,無報(bào)錯(cuò)。說明a.com可以引用b.com里的main.js文件(只是引用肴盏,并未讀寫)科盛。
-
測(cè)試二結(jié)果:頁(yè)面無彈框,控制臺(tái)報(bào)錯(cuò)菜皂。受同源策略的限制贞绵,a.com不能讀寫b.com域名下文件,因而控制臺(tái)會(huì)報(bào)錯(cuò)恍飘,同時(shí)main.js里的腳本也不會(huì)執(zhí)行
-
測(cè)試三結(jié)果:頁(yè)面alert(1),控制臺(tái)打印“alert(1)”
a.com/index通過ajax請(qǐng)求了a.com域名下的main.js文件榨崩,說明相同域名之下,文件是可以互相讀寫的常侣。
2. 至少使用一種方式解決跨域問題
- 方法一:document.domain+iframe
對(duì)于主域相同而子域不同的例子蜡饵,可以通過設(shè)置document.domain的辦法來解決。如a.com
和child1.a.com
,可以在這兩個(gè)文件中分別加上document.domain = 'a.com'
胳施,然后通過a.html文件中創(chuàng)建一個(gè)iframe溯祸,去控制iframe的contentDocument。示例:host文件添加127.0.0.1 child1.a.com
,新建index1文件焦辅,內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index1</title>
</head>
<body>
<h1>hello world</h1>
<iframe src="http://child1.a.com/tests/cross-origin/index2.html" frameborder="0" style="background-color: #ccc"></iframe>
<script type="text/javascript">
window.onload = function(){
document.domain = 'a.com';
var iframe = window.frames[0];
console.log(iframe.window.name);
}
</script>
</body>
</html>
index2文件內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index2</title>
</head>
<body background="#ccc">
<p>index2</p>
<script type="text/javascript">
document.domain = "a.com";
window.name = "index2"
</script>
</body>
</html>
瀏覽器地址欄輸入a.com/tests/cross-origin/index1.html
博杖,可以看到,控制臺(tái)打印出了“index2”, 即筷登,通過document.domain方法剃根,a.com讀取到了指向child1.a.com的iframe窗口的window.name屬性的值。
- 方法二:JSONP
雖然瀏覽器默認(rèn)禁止了跨域訪問前方,但并不禁止在頁(yè)面中引用其他域的JS文件狈醉,并可以自由執(zhí)行引入的JS文件中的function(包括操作cookie、Dom等等)惠险。根據(jù)這一點(diǎn)苗傅,可以方便地通過創(chuàng)建script節(jié)點(diǎn)的方法來實(shí)現(xiàn)完全跨域的通信。以下演示a.com域名下的JSONP.html引用b.com域名下的server.php文件
JSONP.html文件內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP</title>
</head>
<body>
<script type="text/javascript">
window.foo = function(data){
alert(data);
}
</script>
<!-- 動(dòng)態(tài)引用 -->
<script type="text/javascript">
var script = document.createElement('script');
script.src = '//b.com/tests/cross-origin/server.php?callback=foo';
document.body.appendChild(script);
</script>
<!-- 靜態(tài)引用班巩,和動(dòng)態(tài)引用無本質(zhì)差別 -->
<!-- <script type="text/javascript" src="http://b.com/tests/cross-origin/server.php?callback=foo"></script> -->
</body>
</html>
server.php文件內(nèi)容如下:
<?php
$callback = $_GET["callback"];
echo $callback . '("來自b.com里的數(shù)據(jù)")'; //foo("來自b.com里的數(shù)據(jù)")
?>
瀏覽器地址欄輸入//a.com/tests/cross-origin/JSONP.html,可以看到如下圖彈框渣慕,說明通過jsonp已經(jīng)成功得到了b.com下的數(shù)據(jù)。
- 方法三:CORS
以下演示a.com下的CORS.html通過ajax請(qǐng)求b.com域名下的server-CORS.php文件
CORS.html文件內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS</title>
</head>
<body>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script>
<script type="text/javascript">
$.get("http://b.com/tests/cross-origin/server-CORS.php?callback=小美",function(response){
console.log(response); //小美:22
})
</script>
</body>
</html>
server-CORS.php文件內(nèi)容如下:
<?php
//設(shè)置php頭部信息抱慌,表示允許a.com訪問
header('Access-Control-Allow-Origin:http://a.com');
$arr = array('小明'=>'18','小紅'=>'20','小美'=>'22');
$callback = $_GET["callback"];
echo $callback.':'.$arr[$callback];
?>
瀏覽器地址欄輸入http://a.com/tests/cross-origin/CORS.html
,可以看到控制臺(tái)打印出“小美:22”逊桦,說明通過給php設(shè)置Access-Control-Allow-Origin頭部信息可以實(shí)現(xiàn)跨域。
- 方法四:使用HTML5 postMessage
a.com下h5-index.html中的代碼:
<iframe id="ifr" src="http://b.com/tests/cross-origin/h5-index2.html" style="background-color: #ccc"></iframe>
<script type="text/javascript">
window.onload = function(){
var ifr = document.getElementById('ifr');
var targetOrigin = 'http://b.com';
ifr.contentWindow.postMessage('我在這里',targetOrigin);
}
</script>
b.com下h5-index2.html中的代碼:
<script type="text/javascript">
window.addEventListener('message',function(event){
// 通過origin屬性判斷消息來源地址
if(event.origin = 'http://a.com'){
alert(event.data); // 彈出"我在這里"
alert(event.source); // 對(duì)a.com抑进、index.html中window對(duì)象的引用强经。
//但由于同源策略,這里event.source不可以訪問window對(duì)象
}
})
</script>
最終效果: