形成跨域的兩種策略
瀏覽器的同源策略會致使跨域,這裏同源策略又分爲如下兩種javascript
- DOM同源策略:禁止對不一樣源頁面DOM進行操做。這裏主要場景是iframe跨域的狀況,不一樣域名的iframe是限制互相訪問的。
- XmlHttpRequest同源策略:禁止使用XHR對象向不一樣源的服務器地址發起HTTP請求。
只要協議、域名、端口有任何一個不一樣,都被看成是不一樣的域,之間的請求就是跨域操做。
爲何要有跨域限制
瞭解完跨域以後,想必你們都會有這麼一個思考,爲何要有跨域的限制,瀏覽器這麼作是出於何種緣由呢。其實仔細想想就會明白,跨域限制主要是爲了安全考慮。html
AJAX同源策略主要用來防止CSRF攻擊。若是沒有AJAX同源策略,至關危險,咱們發起的每一次HTTP請求都會帶上請求地址對應的cookie,那麼能夠作以下攻擊:java
- 用戶登陸了本身的銀行頁面 http://mybank.com,http://mybank.com向用戶的cookie中添加用戶標識。
- 用戶瀏覽了惡意頁面 http://evil.com。執行了頁面中的惡意AJAX請求代碼。
- http://evil.com向http://mybank.com發起AJAX HTTP請求,請求會默認把http://mybank.com對應cookie也同時發送過去。
- 銀行頁面從發送的cookie中提取用戶標識,驗證用戶無誤,response中返回請求數據。此時數據就泄露了。
- 並且因爲Ajax在後臺執行,用戶沒法感知這一過程。
DOM同源策略也同樣,若是iframe之間能夠跨域訪問,能夠這樣攻擊:ajax
- 作一個假網站,裏面用iframe嵌套一個銀行網站 http://mybank.com。
- 把iframe寬高啥的調整到頁面所有,這樣用戶進來除了域名,別的部分和銀行的網站沒有任何差異。
- 這時若是用戶輸入帳號密碼,咱們的主網站能夠跨域訪問到http://mybank.com的dom節點,就能夠拿到用戶的輸入了,那麼就完成了一次攻擊。
因此說有了跨域跨域限制以後,咱們才能更安全的上網了。json
跨域的解決方式
> 跨域資源共享
CORS是一個W3C標準,全稱是」跨域資源共享」(Cross-origin resource sharing)。 對於這個方式,阮一峯老師總結的文章特別好,但願深刻了解的能夠看一下http://www.ruanyifeng.com/blog/2016/04/cors.html。後端
這裏我就簡單的說一說大致流程。api
- 對於客戶端,咱們仍是正常使用xhr對象發送ajax請求。
惟一須要注意的是,咱們須要設置咱們的xhr屬性withCredentials爲true,否則的話,cookie是帶不過去的哦,設置: xhr.withCredentials = true; - 對於服務器端,須要在 response header中設置以下兩個字段:
Access-Control-Allow-Origin: http://www.yourhost.com
Access-Control-Allow-Credentials:true
這樣,咱們就能夠跨域請求接口了。
> jsonp實現跨域
基本原理就是經過動態建立script標籤,而後利用src屬性進行跨域。跨域
這麼說比較模糊,咱們來看個例子:瀏覽器
// 定義一個fun函數 function fun(fata) { console.log(data); }; // 建立一個腳本,而且告訴後端回調函數名叫fun var body = document.getElementsByTagName('body')[0]; var script = document.gerElement('script'); script.type = 'text/javasctipt'; script.src = 'demo.js?callback=fun'; body.appendChild(script);
返回的js腳本,直接會執行。因此就執行了事先定義好的fun函數了,而且把數據傳入了進來。安全
fun({"name": "name"})
固然,這個只是一個原理演示,實際狀況下,咱們須要動態建立這個fun函數,而且在數據返回的時候銷燬它。
由於在實際使用的時候,咱們用的各類ajax庫,基本都包含了jsonp的封裝,不過咱們仍是要知道一下原理,否則就不知道爲何jsonp不能發post請求了~
> 服務器代理
瀏覽器有跨域限制,可是服務器不存在跨域問題,因此能夠由服務器請求所要域的資源再返回給客戶端。
服務器代理是萬能的。
> document.domain來跨子域
對於主域名相同,而子域名不一樣的狀況,可使用document.domain來跨域 這種方式很是適用於iframe跨域的狀況,直接看例子吧 好比a頁面地址爲 http://a.yourhost.com b頁面爲 http://b.yourhost.com。 這樣就能夠經過分別給兩個頁面設置 document.domain = http://yourhost.com 來實現跨域。 以後,就能夠經過 parent 或者 window[‘iframename’]等方式去拿到iframe的window對象了。
使用window.name進行跨域
window.name跨域一樣是受到同源策略限制,父框架和子框架的src必須指向統一域名。window.name的優點在於,name的值在不一樣的頁面(或者不一樣的域名),加載後仍然存在,除非你顯示的更改。而且支持的長度達到2M.
//a頁面的代碼 <script type="text/javascript"> iframe = document.createElement('iframe'); iframe.style.display = 'none'; var state = 0; iframe.onload = function() { if(state === 1) { var data = iframe.contentWindow.name; console.log(data); iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; iframe.contentWindow.location = 'http://m.zhuanzhuan.58.com:8887/b.html'; } }; document.body.appendChild(iframe); </script>
//b頁面代碼 <script type="text/javascript"> window.name = "hello"; </script>
> location.hash跨域
location.hash方式跨域,是子框架具備修改父框架src的hash值,經過這個屬性進行傳遞數據,且更改hash值,頁面不會刷新。可是傳遞的數據的字節數是有限的。
//a頁面的代碼 <script type="text/javascript"> iframe = document.createElement('iframe'); iframe.style.display = 'none'; var state = 0; iframe.onload = function() { if(state === 1) { var data = window.location.hash; console.log(data); iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; iframe.contentWindow.location = 'http://m.zhuanzhuan.58.com:8887/b.html'; } }; document.body.appendChild(iframe); </script>
//b頁面代碼 <script type="text/javascript"> parent.location.hash = "world"; </script>
> 使用postMessage實現頁面之間通訊
信息傳遞除了客戶端與服務器以前的傳遞,還存在如下幾個問題:
- 頁面和新開的窗口的數據交互。
- 多窗口之間的數據交互。
- 頁面與所嵌套的iframe之間的信息傳遞。
window.postMessage是一個HTML5的api,容許兩個窗口之間進行跨域發送消息。這個應該就是之後解決dom跨域通用方法了,具體能夠參照MDN。
補充: 其實還有一些方法,好比window.name和location.hash。就很適用於iframe的跨域,不過iframe用的比較少了,因此這些方法也就有點過期了。
這些就是我對跨域的瞭解了,實際狀況下,通常用cors,jsonp等常見方法就能夠了。不過遇到了一些很是規狀況,咱們仍是須要知道有更多的方法能夠選擇的