一、什么是跨域?
跨域,指的是瀏覽器不能執行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript施加的安全限制。
所謂同源是指,域名,協議,端口均相同,不明白沒關系,舉個例子:
http://www.123.com/index.html 調用 http://www.123.com/server.PHP (非跨域)
http://www.123.com/index.html 調用 http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 調用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html 調用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
http://www.123.com/index.html 調用 https://www.123.com/server.php (協議不同:http/https,跨域)
請注意:localhost和127.0.0.1雖然都指向本機,但也屬於跨域。
瀏覽器執行javascript腳本時,會檢查這個腳本屬於哪個頁面,如果不是同源頁面,就不會被執行。
你可以理解為兩個域名之間不能跨過域名來發送請求或者請求數據,否則就是不安全的,這種不安全也就是CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF。
一張圖解釋什么是CSRF
二、解決跨域6種辦法:
2.1 JSONP:
使用方式就不贅述了,但是要注意JSONP只支持GET請求,不支持POST請求。
2.2 代理:
例如www.123.com/index.html需要調用www.456.com/server.php,可以寫一個接口www.123.com/server.php,由這個接口在后端去調用www.456.com/server.php並拿到返回值,然后再返回給index.html,這就是一個代理的模式。相當於繞過了瀏覽器端,自然就不存在跨域問題。
2.3 PHP端修改header(XHR2方式)
在php接口腳本中加入以下兩句即可:
header('Access-Control-Allow-Origin:*');//允許所有來源訪問
header('Access-Control-Allow-Method:POST,GET');//允許訪問的方式
2.4 iframe
所以跨域通信其實很簡單,在iframe和主頁里都不斷地檢測hashtag有沒有變化,一旦有變化,就做出相應的改變。
setInterval(function() { var hashVal = window.location.hash.substr(1); document.body.style.backgroundColor = hashVal; }, 1000);
這么做的問題就是,需要不斷地去檢測hashtag是否改變,效率有點低,如果能通過原生的監聽來實現,就會更加高效和優雅。這里就涉及到另一個iframe特性:可以設置其他iframe的大小,即使是不同域的。而頁面的resize事件是可以監聽的,所以就有了下面這個模型。
主頁面先把消息附加到hashtag,然后改變一個隱藏的(或者頁面外的)iframe的size。這個iframe會監聽resize事件,同時捕獲到hashtag。捕獲到hashtag后(也就是所需的數據),再對hashtag做進一步的處理。處理完后把數據傳到主頁內的一個iframe,或者直接操作該iframe。這樣就比較優雅地完成了跨域操作。
Demo
將以下代碼拷貝到本地的一個html文件,然后雙擊在瀏覽器中打開,看看能不能查單詞。(ajax無法跨協議,這是iframe比ajax強大的地方)
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script> $(function(){ $('#btn').click(function(){ $proxy = $('#proxy'); var src = $proxy.attr('src').split('#')[0]; $proxy.attr('src', src + '#' + $('input[name=it]').val()); $proxy.css('width', $proxy.width()+1+'px'); }); }); </script> </head> <body> <input type="text" name="it"> <button id="btn">Translate</button> <p></p> <iframe src="http://demo.leezhong.com/crossdomain/proxy.html" name="proxy" id="proxy" style="position:absolute; top:-10px; width:1px; height:1px"></iframe> <iframe src="http://demo.leezhong.com/crossdomain/show.html" name="show" id="show" style="width:60%;height:300px"></iframe> </body> </html>
2.5 通過 window.name 實現跨域
這篇文章是對 JavaScript跨域總結與解決辦法 的補充。
有三個頁面:
- a.com/app.html:應用頁面。
- a.com/proxy.html:代理文件,一般是一個沒有任何內容的html文件,需要和應用頁面在同一域下。
- b.com/data.html:應用頁面需要獲取數據的頁面,可稱為數據頁面。
實現起來基本步驟如下:
1、在應用頁面(a.com/app.html)中創建一個iframe,把其src指向數據頁面(b.com/data.html)。
數據頁面會把數據附加到這個iframe的window.name上,data.html代碼如下:
<script type="text/javascript"> window.name = 'I was there!'; // 這里是要傳輸的數據,大小一般為2M,IE和firefox下可以大至32M左右 // 數據格式可以自定義,如json、字符串 </script>
2、在應用頁面(a.com/app.html)中監聽iframe的onload事件,在此事件中設置這個iframe的src指向本地域的代理文件(代理文件和應用頁面在同一域下,所以可以相互通信)。app.html部分代碼如下:
<script type="text/javascript"> var state = 0, iframe = document.createElement('iframe'), loadfn = function() { if (state === 1) { var data = iframe.contentWindow.name; // 讀取數據 alert(data); //彈出'I was there!' } else if (state === 0) { state = 1; iframe.contentWindow.location = "http://a.com/proxy.html"; // 設置的代理文件 } }; iframe.src = 'http://b.com/data.html'; if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe); </script>
3、獲取數據以后銷毀這個iframe,釋放內存;這也保證了安全(不被其他域frame js訪問)。
<script type="text/javascript"> iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); </script>
總結起來即:iframe的src屬性由外域轉向本地域,跨域數據即由iframe的window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操作。
2.6 HTML5中最炫酷的API之一(window.postMessage)
就是 跨文檔消息傳輸Cross Document Messaging。高級瀏覽器Internet Explorer 8+, chrome,Firefox , Opera 和 Safari 都將支持這個功能。這個功能實現也非常簡單主要包括接受信息的”message”事件和發送消息的”postMessage”方法。