這一節,咱們來說一講,前端跨域的那些事,主要分紅這樣的幾部分來說解,javascript
1、爲何要跨域?php
2、常見的幾種跨域與使用場景css
2.1 JSONP跨域html
2.2 iframe跨域前端
2.3 window.name 跨域java
2.4 document.domain 跨域jquery
2.5 cookie跨域ajax
2.6 postMessage跨域json
3、總結後端
1、爲何要跨域
跨域,一般狀況下是說在兩個不經過的域名下面沒法進行正常的通訊,或者說是沒法獲取其餘域名下面的數據,這個主要的緣由是,瀏覽器出於安全問題的考慮,採用了同源策略,經過瀏覽器對JS的限制,防止惡意用戶獲取非法的數據。好比這樣的一個場景,惡意用戶仿造一個銀行的官網,在用戶輸入框中嵌套了銀行的頁面,若是是沒有同源策略的限制,那麼惡意用戶則能夠經過這樣的一種方法來獲取銀行用戶的卡號和登陸密碼,這樣對於瀏覽器來講是沒有安全性可言的。同時也能夠有效的規避了大部分的XSS攻擊(XSS攻擊原理:經過向用戶界面中注入script腳本,而後在腳本中獲取用戶的token等身份信息,而後將身份信息發送到惡意用戶指定的地方,在正經常使用戶尚未推出的時候,也就是token等身份信息還有效的時候,經過這些信息強制登陸,將正經常使用戶擠下系統。)
2、常見的幾種跨域與使用場景
前端的跨域主要有:JSONP跨域、iframe跨域、window.name 跨域、document.domain 跨域、cookie跨域、postMessage跨域 後端的跨域:http配置
這裏咱們就主要說明一下前端的跨域,後端的跨域方法不作說明:
跨域方式 特色 侷限
JSONP跨域 一、JSONP跨域不是一種前端技術,而是程序猿創造的一種跨域方法 二、JSONP跨域,是一種簡單的跨域方法,兼容性比較好
iframe 跨域 一、操做簡便 二、兼容性好 單純的使用iframe跨域沒法獲取其餘域名下的數據
window.name 跨域 一、必須與iframe配合使用 二、能夠獲取其餘域名下的數據
document.domain 跨域 一、必須保證兩個要跨域的對象是有一個關聯域名 一、只針對兩個跨域對象是關聯域名 二、若是一個域名被攻擊,那麼另一個域名也有安全問題
cookie跨域 一、這種方法跨域的兼容性好,因爲須要一些額外的設置,因此刪除cookie的時候比較繁瑣 必須保證兩個域名爲關聯域名
postMessage跨域 一、這種方法能夠直接實現將數據從A站點傳輸到B站點,並且解除了cookie的限制和JSONP沒法獲取要傳入的站點的信息 這個爲HTML5新增長的特性,瀏覽器的兼容性不是很好,低版本的瀏覽器不支持,具體能夠見:CANIUSE網站
2.1 JSONP跨域
JSONP跨域主要的依據是利用一些HTML標籤的「漏洞」,而後經過跨域的方式去調用這個在別的域名下面的腳本文件,JSONP跨域有script跨域
咱們先來一個簡單的例子,咱們先下載一個phpStudy,而後配置兩個本地服務器,分別爲:www.test1.com、www.test2.com
在www.test1.com域名下面咱們添加一個test.js文件,內容以下:
alert("test!");
而後在www.test2.com域名下面咱們添加一個HTML文件,以下所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script src="http://www.test1.com/test.js" /> </body> </html>
這個相信大多數人都是見過的,可能你會說這個不就是簡單的script腳本的引入嗎?對的,就是由於script支持跨域,這個也就是咱們常見的CDN。可是上面的例子只能說是一個簡單的JSONP跨域的應用而已,跨域的目的是要實現數據的傳輸。咱們能夠按照這個思路這樣去改寫:
首先咱們能夠在一個域名下面書寫一個方法,而後經過在外部加載一個腳原本調用這個方法,向這個方法中去傳值,這樣就能夠實現把外部的數據傳到當前的域名的下面,從而實現了跨域。
咱們在test.js文件下面定義一個
test("tthis is js load script");
www.test2.com下面的HTML文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script type="text/javascript"> var test=function(data){ alert(data); } </script> <script type="text/javascript" src="DEMO.js"></script> </body> </html>
這個咱們能夠理解爲是在一個HMTL文件中書寫一個test方法,而後test方法容許調用的時候向裏面傳參,以後經過外部加載一個JS文件,JS文件實現的邏輯是調用這個方法,因此咱們不可以把定義的test方法放在調用的DEMO.js的文件後面,由於若是咱們這樣作了的話,那麼在調用外部的JS文件的時候,test方法沒有加載進來因此咱們便看不到效果所在,這個有興趣的同窗能夠本身去試驗一下,這裏就不作試驗了。
在實際的應用中,這樣的例子是基本上不可能看到的,由於這樣的參數傳遞太過於死板,應用性不強,咱們在實際的應用中應該要達到的效果是能夠根據咱們的需求作出相應的響應,這樣說吧,你們可能都知道或者調用過接口吧,接口其實咱們能夠理解爲就是JSONP的一種應用,接下來咱們就來試驗一下:
說了這麼多,你們應該仍是對具體怎樣使用有點雲裏霧裏的吧,如今咱們就基於JSONP的思想來製做一個簡易的音樂專輯查詢器
首先咱們應該要找到一個能夠查詢專輯的公共接口
http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w=關鍵字&perpage=1&ie=utf-8
而後咱們就編寫以下的HTML代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP2</title> </head> <body> <input type="text" id="song" name=""> <input type="button" id="song_search" value="歌曲搜索" name=""> <br /> <div style="width:200px;height:200px;background:pink" id="song_list"></div> <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript"> var searchJsonCallback=function(data){ //遍歷查詢結果 var alb_html=''; for(var i in data.list){ alb_html+='<span>專輯:</span><div style="color:black">'+data.list[0].albumname+'</div>'; } $("#song_list").html(alb_html); } $("#song_search").on("click",function(){ var keyword=$("#song").val(); if(keyword==undefined||keyword==""){ alert("歌曲搜索不能爲空"); return false; }else{ var url = "http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w="+keyword+"&perpage=1&ie=utf-8"; // 建立script標籤,設置其屬性 var script = document.createElement('script'); script.setAttribute('src', url); // 把script標籤加入head,此時調用開始 document.getElementsByTagName('head')[0].appendChild(script); } }) </script> </body> </html>
效果以下:
這個就是JSOP在實際中的應用,若是直接用ajax來實現的話,原理也是同樣,只不過要把script標籤換成ajax調用而已。
若是你要所有使用jquery來實現的話,那麼只須要把ajax中的dataType類型換成jsonp便可(經過上面的例子咱們知道script加載實際上也是一個get請求,這個能夠在network中驗證),這個有興趣的同窗能夠查一查資料。
2.2 iframe跨域
iframe跨域的原理跟script跨域同樣,可是咱們要注意的是標籤自身功能的差別性,具體差別以下:
一、script單純就是引入的做用,可是iframe標籤還有一個做用是顯示的做用能夠把遠程加載的HTML代碼顯示出來,也就是script沒法引入HTML代碼文件
二、script標籤只可以從遠程獲取數據,沒法操做遠程文件執行。可是iframe能夠這樣
上面的第二點提及來有點難理解,咱們就經過一個例子來講明一下:
假設有這樣的一個需求咱們須要在www.jsonp1.com下面調用一個方法來清除josnp2.com下面的本地存儲,
首先咱們在jsonp1.com下面的index.html中加入script標籤去調用jsonp2.com下面的js文件
具體以下:
window.localStorage.clear(); alert(1);
結果是:
咱們能夠看到內容爲1的彈窗,可是在jsonp2的本地存儲就沒有被清除,因此咱們能夠得出結論,script標籤的跨域適用於從遠程獲取數據,不適用對遠程的操做。
這個需求咱們可使用iframe標籤來實現:
jsonp1.com中的HTML文件以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jsonp1</title> </head> <body> <iframe src="http://www.jsonp2.com/demo.html"></iframe> </body> </html>
jsonp2下面的demo.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script type="text/javascript"> window.localStorage.clear(); alert("清除成功"); </script> </body> </html>
這樣咱們就實現了經過在JSONP1中調用JSONP2中的文件來清除JSONP2中的本地存儲
可是你們想想,咱們除了使用這種方法來控制遠程操做以外,咱們還能夠像script跨域同樣來獲取本地存儲的數據嗎?
答案是理論上是不能夠實現的(本例子爲博主的思路的構思,沒有具體實踐過,若有錯誤望各位指出),如圖所示:
首先咱們就在jsonp1.com網站下面的index.html文件下面經過iframe插入jsonp2.com/index.html,這樣咱們就使用了iframe跨域,可是因爲同源策略的限制,因此沒法將jsonp2.com/index.html的值回傳給jsonp1.com/index.html,因此這個時候在jsonp2.com/index.html是能夠獲取到站點的本地存儲的,咱們就能夠像上面音樂接口去使用,把本地儲存中的數值,傳遞過去,可是這個時候jsonp1.com/index.html文件是沒法直接獲取接口中返回的東西的,也沒法經過jsonp2.com/index.html回調,因此這種方法是不可行的
可是咱們就真的沒法實現這樣一個獲取遠程的站點的本地存儲的功能嗎?不是的這個時候利用window.name方法結合iframe來實現跨域
2.3 window.name 跨域
www.jsonp1.com下面的index.html代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>跨域獲取數據</title> <script type="text/javascript"> function domainData(url, fn) { var isFirst = true; var iframe = document.createElement('iframe'); iframe.style.width=0; iframe.style.height=0; iframe.style.display = 'none'; var loadfn = function(){ if(isFirst){ iframe.contentWindow.location = 'http://www.jsonp1.com/proxy.html'; isFirst = false; } else { //alert(1); //console.log(iframe.contentWindow.name); alert(iframe.contentWindow.name); iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); iframe.src = ''; iframe = null; } }; iframe.src = url; if(iframe.attachEvent){ iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe); } </script> </head> <body> </body> <script type="text/javascript"> domainData('http://www.jsonp2.com/demo.html', function(data){ alert(data); }); </script> </html>
www.jsonp2.com/demo.html代碼以下:
<script type="text/javascript"> window.localStorage.setItem("test","123"); var data=window.localStorage.getItem("test"); window.localStorage.clear(); window.name=data; </script>
這樣咱們即可以實如今JSONP1的域名下面訪問到JSONP2中的本地存儲了,你們特別高興有沒有,反正這個需求當時作得時候也是挺困擾個人。
這裏有一些注意事項要特別說明:
一、這個是在網上通過查找的代碼,也能夠說是一個比較標準的使用代碼,你們在使用的時候能夠參照業務需求進行該造。
二、在使用iframe這個標籤以後要進行銷燬,避免出現安全問題
三、window.name的使用必須創建在http協議的基礎之上的,換句話來講就是不能直接打開網頁必定要配置相應的本地域名(直接打開本地網頁採用的是file協議)
2.4 document.domain 跨域
這個的實現思路跟cookie跨域類似,都是在兩個關聯域名中設置document.domain值,而後讓這兩個值相等,這樣就能夠實現跨域操做,具體實現不給出,自行百度
2.5 cookie跨域
cookie跨域這個沒有什麼好說的,不清楚的同窗請看我以前寫過的一篇文章:cookie學習指南
2.6 postMessage跨域
咱們仍是來實現一個上面的功能,在jsonp1域名下面獲取jsonp2中localStorage的test字段的值,嘗試着用postMessage來實現,具體的實現方式以下:
jsonp1.com下面的index.htm以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="test"></div> <textarea id="textarea"></textarea> <iframe style="width:0px;height:0px" id="f" src="http://www.jsonp2.com/demo.html"></iframe> <script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.1/jquery.js"></script> <script> var test1=''; onmessage=function(e){ e=e||event; // console.log(e); // console.log(e.data); test1=e.data; if(test1=="123"){ alert("success!"); }else{ alert("error"); } $("#test").html("<span style='color:red'>"+test1+"</span>"); }; </script> </body> </html>
jsonp2.com中的demo.html內容:
<iframe id="f" src="http://www.jsonp1.com/index.html"></iframe> <script> var f=document.getElementById("f"); f.onload=function(){ window.localStorage.setItem("test","123"); var value=window.localStorage.getItem("test"); window.localStorage.clear(); f.contentWindow.postMessage(value,"http://www.jsonp1.com"); } </script>
這樣就實現了一個從Jsonp2中獲取本地存儲的功能,可是在實踐的過程當中存在的一些問題須要引發咱們的留意:
一、在兩個須要跨域的文件都須要引入一個iframe來加載對方的路徑
二、咱們在使用的時候,是使用postMessage來發送信息給對方,而後咱們是經過監控message事件來獲取信息的
3、總結
這個篇文章主要總結了一些關於前端跨域方面的工做實踐,與一些問題的探索,同時若是發現錯誤的話, 也但願各位可以指正。