1照棋、JSONP
受瀏覽器同源策略的限制,網(wǎng)頁無法向其他域發(fā)送ajax請(qǐng)求烈炭,但在頁面中引入其他域的腳本是可以的,最常見的例子就是在頁面中引入cdn服務(wù)器上的js文件及圖片等靜態(tài)資源趴捅,JSONP正是利用這一特性實(shí)現(xiàn)了跨域。
例如拱绑,http://test.com/test.html獲取http://abc.test.com/data.php上的數(shù)據(jù),那么只需要在test.html中加入以下代碼即可:
<script>
function handleData(data){
// do something
}
</script>
<script scr="http://abc.test.com/data.php?callback=handleData"></script>
其實(shí)第二個(gè)標(biāo)簽返回的是一個(gè)可執(zhí)行的js文件膀藐,data.php包含這樣一段代碼:
<?php
$callback = $_GET['callback'];
$data = array(1,2);
echo $callback.'('.json_encode($data)')'
?>
當(dāng)?shù)诙€(gè)標(biāo)簽加載完畢后红省,就會(huì)執(zhí)行handleData([1,2])
2、document.domain
瀏覽器同源策略中第二個(gè)限制是不同域的框架之間不能進(jìn)行js交互(但可獲得彼此的window對(duì)象)
例如吧恃,在http://abc.test.com/test.html這個(gè)頁面中有一個(gè)iframe起src為http://bcd.test.com/test2.html,以下代碼會(huì)出現(xiàn)注釋中的問題
//test.html
<script>
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //獲得iframe的window對(duì)象(但屬性和方法幾乎不可用)
console.log(ifrWin.document); //無法獲得document屬性
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" ></iframe>
此時(shí)可通過document.domain設(shè)置當(dāng)前文檔的原始域?qū)崿F(xiàn)跨域操作,只要兩個(gè)頁面的原始域相同蚜枢,就可通過js獲得iframe中的各種屬性和方法了.
<!-- test.html -->
<script>
document.domain='test.com'
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //獲得iframe的window對(duì)象(但屬性和方法幾乎不可用)
console.log(ifrWin.document); //正常
}
</script>
<iframe src="https:www.baidu.com" id="iframe" onload="ifrLoad()" ></iframe>
<!-- test2.html -->
<script>
document.domain='test.com'
</script>
需要要注意的是针饥,d只能把document.domain設(shè)置成自身或更高一級(jí)的父域,且主域必須相同丁眼。例如:a.b.test.com 中某個(gè)文檔的document.domain 可以設(shè)成a.b.test.com、b.test.com 藐守、test.com中的任意一個(gè),但是不可以設(shè)成 c.a.b.test.com,因?yàn)檫@是當(dāng)前域的子域卢厂,也不可以設(shè)成example.com,因?yàn)橹饔蛞呀?jīng)不相同了惠啄。
3 、window.name
window.name屬性在不同的頁面(甚至不同域名)加載后依舊存在(如果沒修改則值不會(huì)變化)撵渡,并且可以支持非常長的 name 值(2MB)假設(shè)我們?cè)诎俣仁醉撛O(shè)置了window.name為'baidu',
我們?cè)谕淮翱诘刂窓谥休斎牍雀杈W(wǎng)址趋距,此時(shí)我們查看window.name會(huì)發(fā)現(xiàn)window.name依然為‘baidu’
此時(shí)我們修改window.name為'google',再次打開百度,發(fā)現(xiàn)window.name變?yōu)榱恕甮oogle’
了解了window.name屬性之后外盯,我們想要從test頁面中獲得test1中的數(shù)據(jù)就容易多了,可以首先加載test1頁面门怪,將test頁面需要的數(shù)據(jù)存在window.name中,然后加載test頁面肋殴,但貌似這種方式很蠢坦弟,解決辦法是在test頁面中設(shè)置一個(gè)隱藏的iframe標(biāo)簽用于獲取test2頁面的window.name,但需要注意的是受同源策略的限制护锤,需要在iframe中的test2加載完畢后,在iframe中加載與test同源的test3頁面烙懦。
<!-- http://abc.test.com/test.html -->
<script>
function loadData(){
var iframe=document.getElementById('iframe');
iframe.onload=fucntion(){
var data=iframe.contentWindow.name
console.log(data)
}
iframe.src='http://abc.test.com/test3.html'
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="loadData()" ></iframe>
4赤炒、window.postMessage
window.postMessage是HTML5的API,允許跨域在兩個(gè)窗口/frames之間發(fā)送數(shù)據(jù)莺褒。需要注意的是調(diào)用postMessage方法的window對(duì)象是指接受消息的那個(gè)window對(duì)象。其調(diào)用方式如下:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow 是其他窗口的一個(gè)引用你辣,比如iframe的contentWindow屬性尘执、執(zhí)行window.open返回的窗口對(duì)象、或者是命名過或數(shù)值索引的window.frames誊锭。
argetOrigin 是用來接收消息的那個(gè)window對(duì)象所在的域,如果不希望做限定丧靡,可以用*代替。
transfer 可選饥追,是一串和message 同時(shí)傳遞的 Transferable 對(duì)象. 這些對(duì)象的所有權(quán)將被轉(zhuǎn)移給消息的接收方罐盔,而發(fā)送一方將不再保有所有權(quán)。
<!-- http://abc.test.com/test.html -->
<script>
function ifrLoad(){
var iframe=document.geElementById('iframe');
iframe.contentWindow.postMessage('hello world!','*')
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" '></iframe>
<!-- test2.html -->
<script>
window.onmessage=function(e){
console.log(e.data)
}
</script>
5 、跨域資源共享(CORS)
服務(wù)器設(shè)置Access-Control-Allow-OriginHTTP響應(yīng)頭之后六孵,瀏覽器將會(huì)允許跨域請(qǐng)求幅骄,但需要瀏覽器需要支持HTML5,可以支持POST拆座,PUT等方法
HTML5標(biāo)準(zhǔn)中提出的跨域資源共享(Cross Origin Resource Share,CORS)支持其他的HTTP方法如PUT, POST等挪凑,可以從本質(zhì)上解決跨域問題。
例如搞旭,從http://a.com要訪問http://b.com的數(shù)據(jù)菇绵,通常情況下Chrome會(huì)因跨域請(qǐng)求而報(bào)錯(cuò):
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
錯(cuò)誤原因是被請(qǐng)求資源沒有設(shè)置Access-Control-Allow-Origin,所以我們?cè)赽.com的服務(wù)器中設(shè)置這個(gè)響應(yīng)頭字段即可:
Access-Control-Allow-Origin: * # 允許所有域名訪問咬最,或者
Access-Control-Allow-Origin: http://a.com # 只允許所有域名訪問
為 xhr設(shè)置 withCredentials后CORS方法跨域還可攜帶Cookie,但 PUT/POST 請(qǐng)求需要注意處理 preflight 請(qǐng)求。
6、Proxy
可以在服務(wù)器端設(shè)置一個(gè)代理翁垂,由服務(wù)器端向跨域下的網(wǎng)站發(fā)出請(qǐng)求,再將請(qǐng)求結(jié)果返回給前端沿猜,成功避免同源策略的限制。