Ajax操作如何實現跨域請求?
Ajax (XMLHttpRequest)請求受到同源策略的限制。
Ajax通過XMLHttpRequest能夠與遠程的服務器進行信息交互,另外XMLHttpRequest是一個純粹的Javascript對象,這樣的交互過程,是在后台進行的,用戶不易察覺。
因此,XMLHTTP實際上已經突破了原有的Javascript的安全限制。
舉個例子:
假設某網站引用了其它站點的javascript,這個站點被入侵並在javascript中加入獲取用戶輸入並通過ajax提交給其他站點,這樣就可以源源不斷收集信息。
或者某網站因為存在漏洞導致XSS注入了javascript腳本,這個腳本就可以通過ajax獲取用戶信息並通過ajax提交給其他站點,這樣就可以源源不斷收集信息。
如果我們又想利用XMLHTTP的無刷新異步交互能力,又不願意公然突破Javascript的安全策略,可以選擇的方案就是給XMLHTTP加上嚴格的同源限制。
這樣的安全策略,很類似於Applet的安全策略。IFrame的限制還僅僅是不能訪問跨域HTMLDOM中的數據,而XMLHTTP則根本上限制了跨域請求的提交。(實際上下面提到了CORS已經放寬了限制)
隨着Ajax技術和網絡服務的發展,對跨域的要求也越來越強烈。下面介紹Ajax的跨域技術。
(1)JSONP (JSON with Padding)
JSONP(JSONP是JSON的一種“使用模式”),利用script標簽的src屬性(瀏覽器允許script標簽跨域) 。我們知道<script>標簽可以加載跨域的javascript腳本,並且被加載的腳本和當前文檔屬於同一個域。因此在文檔中可以調用/訪問腳本中的數據和函數。如果javascript腳本中的數據是動態生成的,那么只要在文檔中動態創建<script>標簽就可以實現和服務端的數據交互。
JSONP就是利用<script>標簽的跨域能力實現跨域數據的訪問,請求動態生成的JavaScript腳本同時帶一個callback函數名作為參數。其中callback函數本地文檔的JavaScript函數,服務器端動態生成的腳本會產生數據,並在代碼中以產生的數據為參數調用callback函數。當這段腳本加載到本地文檔時,callback函數就被調用。
這里需要明確的一點是:所謂的域跟js的存放服務器沒有關系,比如baidu.com的頁面加載了google.com的js,那么此js的所在域是baidu.com而不是google.com。也就是說,此時該js能操作baidu.com的頁面對象,而不能操作google.com的頁面對象。
如果還不太明白,這里再詳細解釋一下:
因為通過script標簽引入的js是不受同源策略的限制的
(正如前文提到的baidu.com的頁面加載了google.com的js)
。所以我們可以通過script標簽引入一個js或者是一個其他后綴形式(如
PHP
,jsp等)的文件,此文件返回一個js函數的調用,如返回JSONP_getUsers(["paco","john","lili"]),也就是說此文件返回的結果調用了JSONP_getUsers函數,並且把["paco","john","lili"]傳進去,這個["paco","john","lili"]是一個用戶列表。那么如果此時我們的頁面中有一個JSONP_getUsers函數,那么JSONP_getUsers就被調用到,並且傳入了用戶列表。此時就實現了在本域獲取其他域數據的功能,也就是跨域。
eg:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>Index</h1> 9 <input type="button" onclick="JqAjax();" value="江西tv-list"> 10 <div id="container"></div> 11 <script src="/static/jquery-2.1.4.min.js"></script> 12 <script> 13 function JqAjax() { 14 $.ajax({ 15 url:'http://www.jxntv.cn/data/jmd-jxtv2.html', 16 type:'GET', 17 dataType:'jsonp', 18 jsonp: 'callback', 19 jsonpCallback: 'list', 20 success: function (param) { 21 $.each(param.data,function (i) { 22 var item = param.data[i]; 23 var str = "<p>"+ item.week +"</p>"; 24 var bq = $('#container'); 25 bq.append(str); 26 $.each(item.list,function(j){ 27 var temp = "<span>"+item.list[j].time+"----</span><a href='" + item.list[j].link +"'>" + item.list[j].name +" </a><br/>"; 28 bq.append(temp); 29 }); 30 bq.append("<hr/>"); 31 }) 32 } 33 }) 34 } 35 </script> 36 </body> 37 </html>

為什么script標簽引入的文件不受同源策略的限制?因為script標簽引入的文件內容是不能夠被客戶端的js獲取到的,不會影響到被引用文件的安全,所以沒必要使script標簽引入的文件遵循瀏覽器的同源策略。而通過ajax加載的文件內容是能夠被客戶端js獲取到的,所以ajax必須遵循同源策略,否則被引入文件的內容會泄漏或者存在其他風險。
JSONP的缺點是:它只支持GET請求而不支持POST等其它類型的HTTP請求。不過,一般get請求能完成所有功能。
JSONP易於實現,但是也會存在一些安全隱患,如果第三方的腳本隨意地執行,那么它就可以篡改頁面內容,截獲敏感數據。但是在受信任的雙方傳遞數據,JSONP是非常合適的選擇。可以看出來JSONP跨域一般用於獲取其他域的數據。
(2)CORS
(Cross origin resource sharing
,
即:跨域資源共享
)
通過在HTTP Header中加入擴展字段,服務器在相應網頁頭部加入字段表示允許訪問的domain和HTTP method,客戶端檢查自己的域是否在允許列表中,決定是否處理響應。CORS協議提升了Ajax的跨域能力,但也增加了風險。一旦網站被注入腳本或XSS攻擊,將非常方便的獲取用戶信息並悄悄傳遞出去。
假設我們頁面或者應用已在 http://www.test1.com 上了,而我們打算從 http://www.test2.com 請求提取數據。一般情況下,如果我們直接使用 AJAX 來請求將會失敗,瀏覽器也會返回“源不匹配”的錯誤。
利用 CORS,在 http://www.test2.com 上只需添加一個標頭,就可以允許來自 http://www.test1.com 的請求,下圖是我在PHP中的 hander() 設置,“*”號表示允許任何域向我們的服務端提交請求:
header('Access-Control-Allow-Origin:*');
也可以設置指定的域名,如域名 http://www.test2.com ,那么就允許來自這個域名的請求:
header('Access-Control-Allow-Origin:http://www.test2.com');
