聊聊跨域

http: //zt.jd.com:80/cgi-bin/popuser_menu?tag=4#eduTop
這是一個普通的URL,格式:protocol :// hostname[:port] / path / [?query]#fragment
關係對應以下圖:html

clipboard.png
在瀏覽器裏有一個策略--同源策略--即只有同源的文件之間才能互相通訊,不然就會被瀏覽器拒絕。以下圖,在www.jd.com下面的頁面想請求zt.jd.com頁面時是被拒絕的。web

clipboard.png

同源策略

那什麼是同源,同源就是相同的來源,只有這些資源都來自相同的地方,它們之間才能通訊,不然就可能存在安全和隱私問題。一般狀況下,同源也叫同域,由於判斷同源的方式就是比對「協議+域名+端口「是否一致。ajax

  • protocol: 常見的有http, https, ftp等協議跨域

  • host: 如zt.jd.com,,其中com是頂級域名,jd是主域名,zt是子域名。所以zt.jd.com和www.jd.com是不一樣源的,m.zt.jd.com和zt.jd.com也是不一樣源的。瀏覽器

  • port: 80, 8080等,url不加端口號時默認爲80端口,所以對於http ://zt.jd.com和http ://zt.jd.com:80是同源的,而很明顯http ://zt.jd.com和http ://zt.jd.com:8080則是不一樣源。安全

對於http ://store.company.com/dir/page.html這個URL來講,MDN上有這樣的描述,基本能夠很清晰的說明什麼是同源了:服務器

clipboard.png

Ajax跨域

對於XMLHttpRequest對象,是不支持跨域操做的,你沒辦法在www.jd.com下經過ajax去調用zt.jd.com下的接口,瀏覽器會把請求給拒絕掉。
那瀏覽器究竟是拒絕發請求呢?仍是拒絕接受響應呢?咱們看一下network:cookie

clipboard.png

很明顯請求是發出去的,並且響應了200 ok。只是響應內容被瀏覽器屏蔽了,js也沒法讀取到。fiddler裏面能夠看到響應的內容:框架

clipboard.png
那爲何瀏覽器不直接把請求丟掉,而是在響應的時候屏蔽掉呢?由於原則上來說若是服務器容許,客戶端沒理由阻止跨域,所以客戶端必須確認服務器並無容許當前域的跨域訪問時才能丟掉這個請求,這就涉及到CORS。
在XMLHttpRequest2裏,能夠經過服務器設置Access-control-allow-origin響應頭部字段來告訴瀏覽器容許ajax跨域。以下圖,在zt.jd.com下用ajax調用xoa.pp.jd.com下的接口,成功跨域調用。dom

clipboard.png

clipboard.png

但畢竟是HTML5的新特性,瀏覽器端的兼容性並很差,所以,目前不多使用這種方式。更多的使用JSONP。

clipboard.png

JSONP

咱們知道在HTML標籤裏,有一些特殊的標籤是能夠跨域的,好比script, link, img之類的,這些標籤能夠提供給咱們主動調用第三方資源的能力,方便web開發。因爲調用資源的主動權在你本身手裏,所以只要你調用本身可信任的資源,不會有什麼安全性問題。JSONP就是利用script標籤的跨域功能來實現跨域拉取數據的。 咱們來看script標籤的特殊之處在哪裏。 一般狀況下,script標籤是這樣用的:

clipboard.png
還有這樣用的:

clipboard.png

也就是說script既能夠拉取數據,也能夠執行代碼。那假如,咱們用它來拉取一個跨域的接口,咱們怎樣才能把接口返回的數據拿來用呢?很明顯,那就是讓接口在返回數據的同時,還要返回操做這些數據的js代碼。好比:

clipboard.png

可是這種方式存在兩個弊端:依賴問題和阻塞問題。你必須確保那個拉取數據的script標籤是先執行了,然後面的代碼必須等它執行完才能執行。那麼,就使用異步回調唄。以下:

clipboard.png
先定義一個回調函數,而後把拉取數據的script標籤改成異步拉取。這樣拉取成功後會調用定義好的回調函數來進行數據處理。這樣就不用理會依賴問題,script標籤能夠不考慮前後順序,由於異步必須在同步代碼完成後才執行。可是,這樣還存在一個問題,那就是接口通用性受到了限制,由於必須返回一句寫死的renderData(data)代碼,拉取這個接口的頁面老是必須定義一個叫作renderData的函數。因而,聰明的人又想到了解決辦法,那就是用傳參的方式告訴接口給我返回什麼回調函數,這樣我想定義一個renderData1也行,renderData2也行,接口的通用性就大大提升了。以下:

clipboard.png

接口讀出參數callback,並把它返回便可。這樣我傳給接口render1它就返回render1,render2就返回render2。

clipboard.png

這就是JSONP,通常會封裝成一個相似於ajax的函數,直接傳參便可動態建立script標籤實現跨域請求。可是,這裏有必要說明一點,JSONP接口經過CSRF很容易被惡意拉取到敏感信息,你能夠構造頁面讓目標用戶打開,由於callback的存在,你能將拉取到的用戶信息上傳到本身的服務器,所以需作好防範。

跨域與Iframe

iframe是一個特殊的標籤,能夠在頁面裏面加載別的頁面。
既然有加載別的頁面的能力,那在非同源下一定是要受到同源策略限制的,不然,咱們就能隨便讀取任意網站的cookie了。

  • 非同源狀況下,默認是阻止父框架與子框架之間的通訊的,但能夠分爲兩種狀況:

    • 子框架加載的URL是一個第三方頁面時:你能作的僅僅就是把它展示出來,而沒有任何能力去訪問它。

    • 子框架加載的URL是你本身可控的頁面,這時候能夠分兩種狀況:

      • 主域相同,子域不一樣:能夠經過在iframe和父頁面裏都設置document.domain=主域名來實現跨域通訊。

      • 主域不一樣:這種狀況只能經過一些奇淫異巧來實現簡單的通訊。有兩種狀況:

        • 父框架向子框架通訊:父框架經過改變iframe的src的hash值,iframe監聽自身location.hash便可獲取到父框架設置的參數,從而實現信息的傳遞。但數據量收到url長度的限制。

        • 子框架向父框架通訊:你們或許會想那直接逆向一下設置location.hash不就好了嗎。事實上是不行的,在iframe裏設置location.hash是沒法更新到iframe的src值的。所以,還得再嵌入一個跟父框架同源的iframe到子框架裏,而後經過「父框架向子框架通訊」的方式讓子框架傳遞信息給「孫框架",而後父框架再去讀」孫框架「就好了。

  • 同源狀況下,無需設置便可雙向通訊。可是,假如父頁面和子頁面任何一方設置了document.domain,都會致使瀏覽器的跨域攔截,即便document.domain就是當前的domain也不行。也就是說,要麼都不設置,要麼都設置。

相關文章
相關標籤/搜索