1、JSONP
受瀏覽器同源策略的限制,網頁無法向其他域發送ajax請求,但在頁面中引入其他域的腳本是可以的,最常見的例子就是在頁面中引入cdn服務器上的js文件及圖片等靜態資源,JSONP正是利用這一特性實現了跨域。
例如,http://test.com/test.html獲取http://abc.test.com/data.php上的數據,那么只需要在test.html中加入以下代碼即可:
<script>
function handleData(data){
// do something
}
</script>
<script scr="http://abc.test.com/data.php?callback=handleData"></script>
其實第二個標簽返回的是一個可執行的js文件,data.php包含這樣一段代碼:
<?php
$callback = $_GET['callback'];
$data = array(1,2);
echo $callback.'('.json_encode($data)')'
?>
當第二個標簽加載完畢后,就會執行handleData([1,2])
2、document.domain
瀏覽器同源策略中第二個限制是不同域的框架之間不能進行js交互(但可獲得彼此的window對象)
例如,在http://abc.test.com/test.html這個頁面中有一個iframe起src為http://bcd.test.com/test2.html,以下代碼會出現注釋中的問題
//test.html
<script>
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //獲得iframe的window對象(但屬性和方法幾乎不可用)
console.log(ifrWin.document); //無法獲得document屬性
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" ></iframe>
此時可通過document.domain設置當前文檔的原始域實現跨域操作,只要兩個頁面的原始域相同,就可通過js獲得iframe中的各種屬性和方法了.
<!-- test.html -->
<script>
document.domain='test.com'
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //獲得iframe的window對象(但屬性和方法幾乎不可用)
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設置成自身或更高一級的父域,且主域必須相同。例如:a.b.test.com 中某個文檔的document.domain 可以設成a.b.test.com、b.test.com 、test.com中的任意一個,但是不可以設成 c.a.b.test.com,因為這是當前域的子域,也不可以設成example.com,因為主域已經不相同了。
3 、window.name
window.name屬性在不同的頁面(甚至不同域名)加載后依舊存在(如果沒修改則值不會變化),并且可以支持非常長的 name 值(2MB)假設我們在百度首頁設置了window.name為'baidu',
我們在同一窗口地址欄中輸入谷歌網址,此時我們查看window.name會發現window.name依然為‘baidu’
此時我們修改window.name為'google',再次打開百度,發現window.name變為了‘google’
了解了window.name屬性之后,我們想要從test頁面中獲得test1中的數據就容易多了,可以首先加載test1頁面,將test頁面需要的數據存在window.name中,然后加載test頁面,但貌似這種方式很蠢,解決辦法是在test頁面中設置一個隱藏的iframe標簽用于獲取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,允許跨域在兩個窗口/frames之間發送數據。需要注意的是調用postMessage方法的window對象是指接受消息的那個window對象。其調用方式如下:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow 是其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。
argetOrigin 是用來接收消息的那個window對象所在的域,如果不希望做限定,可以用*代替。
transfer 可選,是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權。
<!-- 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)
服務器設置Access-Control-Allow-OriginHTTP響應頭之后,瀏覽器將會允許跨域請求,但需要瀏覽器需要支持HTML5,可以支持POST,PUT等方法
HTML5標準中提出的跨域資源共享(Cross Origin Resource Share,CORS)支持其他的HTTP方法如PUT, POST等,可以從本質上解決跨域問題。
例如,從http://a.com要訪問http://b.com的數據,通常情況下Chrome會因跨域請求而報錯:
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.
錯誤原因是被請求資源沒有設置Access-Control-Allow-Origin,所以我們在b.com的服務器中設置這個響應頭字段即可:
Access-Control-Allow-Origin: * # 允許所有域名訪問,或者
Access-Control-Allow-Origin: http://a.com # 只允許所有域名訪問
為 xhr設置 withCredentials后CORS方法跨域還可攜帶Cookie,但 PUT/POST 請求需要注意處理 preflight 請求。
6、Proxy
可以在服務器端設置一個代理,由服務器端向跨域下的網站發出請求,再將請求結果返回給前端,成功避免同源策略的限制。