JS跨域問題解決方案
一、JSONP
在js中,我們直接用XMLHttpRequest請求不同域上的資料時,是不可以的。但是,在頁面上引入不同域上的js指令碼檔案卻是可以的,script標籤裡的src屬性來完成的,jsonp正是利用這個特性來實現的。
比如,在桌面新建一個crossDomain.html頁面,它裡面的程式碼需要利用ajax獲取一個不同域上的json資料,假設這個json資料地址是 http://192.168.x.xxx/JSONP/jsonpTest.php 那麼crossDomain.html中的程式碼就可以這樣:
<script type="text/javascript"> var text = document.querySelector('.text'); function dosomething(jsondata) { var str = ""; for (var i = 0; i < jsondata.length; i++) { str += jsondata[i]; } text.innerHTML = '我是JS通過JSONP跨域請求來的資料:'+'<span class="show">'+str+'</span>'; } </script> <script type="text/javascript" src="http://192.168.x.xxx/JSONP/jsonpTest.php?callback=dosomething"></script>
可以看到在獲取資料的地址後面還有一個callback引數,按慣例是用這個引數名,但是你用其他的也一樣。當然如果獲取資料的jsonp地址頁面不是你自己能控制的,就得按照提供資料的那一方的規定格式來操作了。
因為是當做一個js檔案來引入的,所以 http://192.168.x.xxx/JSONP/jsonpTest.php 返回的必須是一個能執行的js檔案,所以這個頁面的php程式碼可能是這樣的:
<?php $callback = $_GET['callback'];//得到回掉函式名 $data = array('a','b','c'); //要返回的資料 echo $callback.'('.json_encode($data).')'; //輸出 ?>
然後在crossDomain.html中打印出返回的jsondata如下:
["a", "b", "c"]
可以看到請求成功了,然後就可以在crossDomain.html這個頁面裡處理這個資料了。
這樣jsonp的原理就很清楚了,通過script標籤引入一個js檔案,這個js檔案載入成功後會執行我們在url引數中指定的函式,並且會把我們需要的json資料作為引數傳入。所以jsonp是需要伺服器端的頁面進行相應的配合的。
當然可以直接用一些已經封裝過的庫,這樣就不用每次去建立script標籤了。如下為JQ的跨域API:
$.getJSON('http://192.168.x.xxx/JSONP/jsonpTest.php?callback=?',function(jsondata){ console.log(jsondata);//["a", "b", "c"] var str = ""; $.each(jsondata,function(i,index){ return str += index; }); $(".text1").html('我是JQ通過JSONP跨域請求來的資料:'+'<span class="show">'+str+'</span>'); });
jquery的getJSON方法會自動生成一個全域性函式來替換callback=?中的問號,之後獲取到資料後又會自動銷燬,實際上就是起一個臨時代理函式的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就呼叫普通的ajax方法;跨域的話,則會以非同步載入js檔案的形式來呼叫jsonp的回撥函式。

二、CORS
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。相比JSONP只能發GET請求,CORS允許任何型別的請求。CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。
整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。
因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。
服務端設定Access-Control-Allow-Origin
這種方式只要服務端把response的header頭中設定 Access-Control-Allow-Origin
為請求當前域名下資料的域名即可。一般情況下設為 * 即可。這樣客戶端就不需要使用jsonp來獲取資料。
會有安全問題( 更多請百度 )
但是個人是用這個方法的
CORS支援POST提交,並且實施起來很簡單,CORS原理:只需要向響應頭header中注入Access-Control-Allow-Origin,這樣瀏覽器檢測到header中的Access-Control-Allow-Origin,則就可以跨域操作。
我用的是 php ,用法如:(*號也可以指定特定的域名,只允許該域名訪問)
<?php header("Access-Control-Allow-Origin:*"); //...
三、通過修改document.domain來跨子域
上面的jsonp是來解決ajax跨域請求的,那麼如果是需要處理 Cookie 和 iframe 該怎麼辦呢?這時候就可以通過修改document.domain來跨子域。兩個網頁一級域名相同,只是二級域名不同,瀏覽器允許通過設定document.domain共享 Cookie或者處理iframe。比如A網頁是 http://w1.example.com/a.html ,B網頁是 http://w2.example.com/b.html ,那麼只要設定相同的document.domain,兩個網頁就可以共享Cookie。
document.domain = 'example.com'; //現在,A網頁通過指令碼設定一個 Cookie。 document.cookie = "test1=hello"; //B網頁就可以讀到這個 Cookie。 var allCookie = document.cookie;
注意, 這種方法只適用於 Cookie 和 iframe 視窗 ,LocalStorage 和 IndexDB 無法通過這種方法,規避同源政策,而要使用下文介紹的PostMessage API。
另外,伺服器也可以在設定Cookie的時候,指定Cookie的所屬域名為一級域名,比如.example.com。
Set-Cookie: key=value; domain=.example.com; path=/ //這樣的話,二級域名和三級域名不用做任何設定,都可以讀取這個Cookie。
不同的iframe 之間(父子或同輩),是能夠獲取到彼此的window物件的,但是你卻不能使用獲取到的window物件的屬性和方法(html5中的postMessage方法是一個例外,還有些瀏覽器比如ie6也可以使用top、parent等少數幾個屬性),總之,你可以當做是隻能獲取到一個幾乎無用的window物件。
首先說明一下同域之間的iframe是可以操作的。比如 http://127.0.0.1/JSONP/a.html 裡面嵌入一個iframe指向 http://127.0.0.1/myPHP/b.html 。那麼在a.html裡面是可以操作iframe裡面的DOM的。
<iframe src="http://127.0.0.1/myPHP/b.html" frameborder="1"></iframe> <body> <script type="text/javascript"> var iframe = document.querySelector("iframe"); iframe.onload = function(){ var win = iframe.contentWindow; var doc = win.document; var ele = doc.querySelector(".text1"); var text = ele.innerHTML="123456"; } </script>
如果兩個網頁不同源,就無法拿到對方的DOM。典型的例子是iframe視窗和window.open方法開啟的視窗,它們與父視窗無法通訊。如果兩個視窗一級域名相同,只是二級域名不同,那麼document.domain屬性,就可以規避同源政策,拿到DOM。
對於完全不同源的網站,目前有三種方法,可以解決跨域視窗的通訊問題。
片段識別符(fragment identifier)
window.name
跨文件通訊API(Cross-document messaging)