1.什么是同源策略
所謂"同源"指的是"三個(gè)相同":(1)協(xié)議相同,(2)域名相同,(3)端口相同
同源策略是瀏覽器的一個(gè)功能买雾,也是瀏覽器安全的基石把曼,最初,它的含義是指漓穿,A網(wǎng)頁(yè)設(shè)置的 Cookie嗤军,B網(wǎng)頁(yè)不能打開(kāi),從而保證用戶信息的安全性晃危。隨著互聯(lián)網(wǎng)的發(fā)展叙赚,"同源政策"越來(lái)越嚴(yán)格。目前僚饭,如果非同源震叮,共有三種行為受到限制:
(1) Cookie、LocalStorage 和 IndexDB 無(wú)法讀取鳍鸵。
(2) DOM 無(wú)法獲得苇瓣。
(3) AJAX 請(qǐng)求不能發(fā)送。
換句話說(shuō):不同源的客戶端腳本在沒(méi)明確授權(quán)的情況下权纤,不能讀寫對(duì)方的資源钓简。
2.什么是跨域?跨域有幾種實(shí)現(xiàn)形式
什么是跨域:跨域即突破同源策略的限制汹想。
跨域有六種實(shí)現(xiàn)方式:
-
1.document.domain+iframe
應(yīng)用范圍:這種辦法只能解決主域相同而子域不同外邓,且是iframe形式的跨域;
存在問(wèn)題:安全性古掏,當(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)求
存在問(wèn)題:存在安全性問(wèn)題丧枪,可被注入可執(zhí)行的js代碼(callback=alert(1)
), 對(duì)于這個(gè)問(wèn)題,只能通過(guò)外界的字符串過(guò)濾來(lái)解決庞萍,如禁止callback中傳入括號(hào)拧烦,使用正則去除左右括號(hào),callback=callback.replace(/\(/g,"")
钝计,callback=callback.replace(/\)/g,"")
-
3.CORS
給被訪問(wèn)方設(shè)置Access-Control-Allow-Origin恋博,如在php文件頭部寫入header('Access-Control-Allow-Origin:http://a.com:8080')
,表示允許來(lái)自源http://a.com:8080
的請(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來(lái)傳值闷煤。url中#號(hào)及其后面的內(nèi)容就是location.hash,改變hash的值頁(yè)面并不會(huì)刷新童芹,所以可以利用hash值來(lái)進(jìn)行數(shù)據(jù)傳遞。
這種方法缺點(diǎn)也很多鲤拿,諸如數(shù)據(jù)直接暴露在了url中辐脖,數(shù)據(jù)容量和類型都有限等。 -
6.利用window.name
主要利用window.name值不隨url改變而改變皆愉,只要當(dāng)前頁(yè)面沒(méi)被關(guān)閉,window.name的值就不會(huì)改變艇抠。
3.jsonp 的原理是什么
JSONP是服務(wù)器與客戶端l跨域通信的常用方法幕庐。最大特點(diǎn)就是簡(jiǎn)單適用,老式瀏覽器全部支持家淤,服務(wù)器改造非常小异剥。
原理:網(wǎng)頁(yè)通過(guò)添加一個(gè)<script>元素,向服務(wù)器請(qǐng)求JSON數(shù)據(jù)絮重,這種做法不受同源政策限制冤寿;服務(wù)器收到請(qǐng)求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)里傳回來(lái)青伤。
4.CORS是什么?
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫督怜。它是W3C標(biāo)準(zhǔn),是跨源AJAX請(qǐng)求的根本解決方法狠角。相比JSONP只能發(fā)GET請(qǐng)求号杠,CORS允許任何類型的請(qǐng)求。
支持情況:CORS需要瀏覽器和服務(wù)器同時(shí)支持丰歌,只要服務(wù)器實(shí)現(xiàn)了CORS接口姨蟋,且瀏覽器支持該功能(目前所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10)立帖,就可以跨域通信眼溶。
對(duì)于開(kāi)發(fā)者來(lái)說(shuō),CORS通信與同源的AJAX通信沒(méi)有差別晓勇,代碼完全一樣堂飞。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息宵蕉,有時(shí)還會(huì)多出一次附加的請(qǐng)求酝静,但用戶不會(huì)有感覺(jué)。
實(shí)際操作
1.本地搭建服務(wù)器羡玛,演示同源策略
這里以Wampserver為例演示别智,具體步驟:
1.在Wampserver軟件的www目錄下新建兩個(gè)文件,index.html和main.js稼稿。其中薄榛,index.html的主要內(nèi)容如下:
<body>
<h1>hello world!</h1>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
// 分別做如下3個(gè)測(cè)試
//測(cè)試一:a.com/index引用b.com域名下的main.js文件
<script src="http://b.com:8080/main.js"></script>
//測(cè)試二:a.com/index通過(guò)ajax請(qǐng)求b.com域名下的main.js文件
<script type="text/javascript">
$.get("http://b.com:8080/main.js",function(response){
console.log(response);
})
</script>
//測(cè)試三:a.com/index 通過(guò)ajax請(qǐng)求a.com域名下的main.js文件
<script type="text/javascript">
$.get("http://a.com:8080/main.js",function(response){
console.log(response);
})
</script>
</body>
main.js的內(nèi)容如下:
alert (1);
2.以管理員權(quán)限運(yùn)行記事本讳窟,用記事本打開(kāi)路徑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
3.啟動(dòng)Wampserver,瀏覽器地址欄輸入a.com:8080
4.測(cè)試結(jié)果:
- 測(cè)試一結(jié)果:頁(yè)面 alert(1)硬猫,無(wú)報(bào)錯(cuò)补箍。
說(shuō)明a.com可以引用b.com里的main.js文件(只是引用,并未讀寫)啸蜜。 - 測(cè)試二結(jié)果:頁(yè)面無(wú)彈框坑雅,控制臺(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通過(guò)ajax請(qǐng)求了a.com域名下的main.js文件蜂林,說(shuō)明相同域名之下遥诉,文件是可以互相讀寫的。
2.至少使用一種方式解決跨域問(wèn)題
方法一:document.domain+iframe
對(duì)于主域相同而子域不同的例子噪叙,可以通過(guò)設(shè)置document.domain的辦法來(lái)解決矮锈。如a.com和child1.a.com,可以在這兩個(gè)文件中分別加上document.domain = ‘a(chǎn).com’,然后通過(guò)a.html文件中創(chuàng)建一個(gè)iframe睁蕾,去控制iframe的contentDocument愕难。
示例:
host文件添加127.0.0.1 child1.a.com ,index文件內(nèi)容如下:
<iframe src="http://child1.a.com:8080/index2.html" frameborder="0"></iframe>
<script type="text/javascript">
window.onload=function(){
document.domain = "a.com"; //降域
var iframe=window.frames[0];
console.log(iframe.window.name);
}
</script>
index2文件內(nèi)容如下:
<p>index2</p>
<script type="text/javascript">
document.domain = "a.com"; //降域
window.name="index2";
</script>
瀏覽器地址欄輸入a.com:8080
惫霸,可以看到猫缭,控制臺(tái)打印出了“index2”, 即,通過(guò)document.domain方法壹店,a.com讀取到了iframe窗口的window.name屬性的值猜丹。
方法二:JSONP
雖然瀏覽器默認(rèn)禁止了跨域訪問(wèn),但并不禁止在頁(yè)面中引用其他域的JS文件硅卢,并可以自由執(zhí)行引入的JS文件中的function(包括操作cookie射窒、Dom等等)。根據(jù)這一點(diǎn)将塑,可以方便地通過(guò)創(chuàng)建script節(jié)點(diǎn)的方法來(lái)實(shí)現(xiàn)完全跨域的通信脉顿。
以下演示a.com/index引用b.com域名下的server.php文件
index文件內(nèi)容如下:
<script>
window.foo=function(data){
alert(data);
}
</script>
//動(dòng)態(tài)引用
<script>
var script=document.createElement("script");
script.src="http://b.com:8080/server.php?callback=foo";
document.body.appendChild(script);
</script>
// 或者靜態(tài)引用,和動(dòng)態(tài)引用沒(méi)有本質(zhì)差別
// <script src="http://b.com:8080/server.php?callback=foo"></script>
server.php文件內(nèi)容如下:
<?php
$callback=$_GET["callback"];
echo $callback . "('來(lái)自b.com的數(shù)據(jù)')"; //foo('來(lái)自b.com的數(shù)據(jù)')
?>
瀏覽器地址欄輸入a.com:8080
,可以看到如下圖彈框点寥,說(shuō)明通過(guò)jsonp已經(jīng)成功得到了b.com下的數(shù)據(jù)艾疟。
方法三:CORS
以下演示a.com/index通過(guò)ajax請(qǐng)求b.com域名下的server.php文件
index文件內(nèi)容如下:
<script>
$.get("http://b.com:8080/server.php?callback=小明",function(response){
console.log(response); //小明:18
})
</script>
server.php文件內(nèi)容如下:
<?php
header('Access-Control-Allow-Origin:http://a.com:8080'); //設(shè)置php頭部信息,表示允許a.com訪問(wèn)
$arr = array('小明' => "18", "花花"=>"16","天天"=>"15");
$callback=$_GET["callback"];
echo $callback .":".$arr[$callback];
?>
瀏覽器地址欄輸入a.com:8080,可以看到控制臺(tái)打印出“小明:18”,說(shuō)明通過(guò)給php設(shè)置Access-Control-Allow-Origin頭部信息可以實(shí)現(xiàn)跨域蔽莱。