純前端解決跨域問題
背景
跨域是由瀏覽器的同源策略引起的,是指頁面請求的url地址,必須與瀏覽器上的url地址處于同域上(即域名,端口,協議相同)。
這是為了防止某域名下的接口被其他域名下的網頁非法調用,是瀏覽器對JavaScript施加的安全限制。
這個措施的出發點是好的,但是程序頁面開發的過程中,卻常常給前端開發者帶來麻煩。
由于前端開發過程中,靜態資源是放在本地電腦上的,訪問這些資源通常通過IP方式(127.0.0.1)或者localhosts來訪問,與線上服務器所在域名不符,不能順利調用服務的端口。
解決跨域問題常用的解決方案有這兩個:
- JSONP:利用script標簽可跨域的特點,在跨域腳本中可以直接回調當前腳本的函數。
- CORS:服務器設置HTTP響應頭中Access-Control-Allow-Origin的值,解除跨域限制。
但是這兩個跨域方案都存在一個致命的缺陷,嚴重依賴后端的協助。
開發中遇到的每一個接口都需要提前找后端進行特殊的處理。而且即使后端愿意幫忙,某些接口不是隨便就能開放的(譬如已經在上線正式環境的接口)。
無論如何,依賴后端協助的跨域解決方案都會在一定程度上限制前端開發的進度。
那么有沒有前端獨立就能實現的跨域方案呢?有的,我們可以利用「代理」或「反向代理」技術來實現開發中的跨域問題。
代理與反向代理
代理
代理,也稱正向代理,意思是一個位于客戶端和目標服務器(target server)之間的服務器,為了從目標服務器取得內容,客戶端向代理發送一個請求并指定目標(目標服務器),然后代理向目標服務器轉交請求并將獲得的內容返回給客戶端。
通俗地說:
- 「客戶端」可以看作一個黑社會大佬,「目標服務器」可以看作一家飯店,「代理服務器」可以看作小弟。
- 「老大」想吃飯店的醬排骨飯,就讓「小弟」去買,「小弟」跑到「飯店」要個醬排骨飯。
- 「飯店」醬排骨飯做好,送到「小弟」手上,「小弟」最后再把醬排骨飯拿給「大佬」。
說白了,小弟就是個跑腿的,代理大佬的需求。
數據流程:
- 數據請求過程:瀏覽器-》代理服務器-》目標服務器
- 數據返回過程:目標服務器-》代理服務器-》瀏覽器
應用:
最經典的應用就是科學上網:我是一個國內用戶,我訪問不了google,但是我能訪問一個香港的某個代理服務器。
這個香港的代理服務器可以訪問google,于是我先把請求發送到那個代理服務器,告訴他我需要訪問google,代理服務器去取內容,最后返回給我。
就好比,大佬被抓起來坐牢了,不能出去買醬排骨,只好拜托小弟去買回來。
反向代理
百度百科的解釋如下:
反向代理(Reverse Proxy)方式是指以代理服務器來接受internet上的連接請求,然后將請求轉發給內部網絡上的服務器,并將從服務器上得到的結果返回給internet上請求連接的客戶端,此時代理服務器對外就表現為一個反向代理服務器。
數據流程:
- 數據請求過程:瀏覽器-》【反向代理服務器-》處理數據的服務器】
- 數據返回過程:【處理數據的服務器-》反向代理服務器】-》瀏覽器
通俗地說:
「瀏覽器」可以看作食客,「【反向代理服務器-》處理數據的服務器】」這一個整體可以看作飯店,其中「反向代理服務」相當于點單的服務員?!柑幚頂祿姆掌鳌箍梢岳斫鉃槭菑N師。
- 「食客」向來到「飯店」向「服務員」點菜,但服務員并不會真正去做菜,他是下達命令讓「廚師」去做菜。
- 「廚師」把菜做好了給「服務員」,「服務員」再把菜端給「食客」。
在外部看來,「代理服務器」和「處理數據的服務器」是一個整體。就好比,食客只會去飯店吃飯,而不是去找廚師吃飯(即對于瀏覽器來說,到達反向代理服務器已經完成任務了,后面的操作則由反向代理服務器負責)。
具體飯店怎么操作,對食客是透明的。有可能某個服務員即當服務器也當廚師(即反向代理服務器和處理數據的服務器是同一臺PC機)。
補充一下,沒有反向代理,就好比沒有了服務員,食客直接向廚師要吃的。譬如,你餓了,直接叫媽媽做飯是一樣的(少了下訂單的步驟)
比較
從用途上來講:
- 正向代理的典型用途是為在防火墻內的局域網客戶端提供訪問Internet的途徑。正向代理還可以使用緩沖特性減少網絡使用率。
- 反向代理的典型用途是為后端的多臺服務器提供負載平衡,或為后端較慢的服務器提供緩沖服務。
從安全性來講:
- 正向代理允許客戶端通過它訪問任意網站并且隱藏客戶端自身,因此你必須采取安全措施以確保僅為經過授權的客戶端提供服務。
- 反向代理對外都是透明的,訪問者并不知道自己訪問的是一個代理。
從使用方來看:
- 正向代理是瀏覽器端進行配置的,與服務器端無關,甚至可以對服務端隱藏。
- 反向代理是服務器端配置的,對瀏覽器端是透明的。
利用代理實現跨域
實現原理
對正向代理服務器進行配置,當獲取非接口數據時,讓代理服務器指向開發者本機的資源。當訪問接口時,訪問后端接口數據。
相當于大佬讓小弟把醬排骨飯里面的飯和醬排骨分開買,飯自己家煮,醬排骨才去飯店買。
程序運行過程
- 瀏覽器訪問頁面,假設訪問淘寶頁面:taobao.com/index.html(假設這個頁面中調用了taobao.com/api/getNew獲取最新商品的接口)
- taobao.com/index.html請求經過代理服務器,根據配置,index.html頁面請求127.0.0.1:3000
- 127.0.0.1:3000返回index.html文件給瀏覽器。
- 瀏覽器運行index.html頁面,發起taobao.com/api/etNew請求。
- taobao.com/api/getNew請求經過代理服務器,但由于沒有對這個接口進行特殊配置,會正常訪問淘寶服務器中的接口。
- 淘寶服務器接受到taobao.com/api/getNew請求,檢查請求頭的hosts字段,發現是taobao.com,沒有跨域,將結果返回給代理服務器。
- 代理服務器拿到結果,返回給瀏覽器,瀏覽器進行解析顯示。
代理配置(以mac下的charles為例)
- 打開charles的映射關系表【charles->tool->Map Remote】。
- 點擊add可以添加映射關系。
- 點擊 ? 符號,可以進入配置規則介紹頁面。
注意:
- 匹配taobao.com/api/ 的配置項要放在匹配taobao.com/ 的配置項前,讓匹配API的優先級更高。否則將只匹配到taobao.com/*的配置。
- 如果接口請求中,有涉及到https協議的,需要提前在電腦上安裝charles的證書 。
- chrome系瀏覽器的請求是不經過charles代理的,這時需要設置電腦上的網絡設置,設置代理地址為charles。
- 微信開發者工具是不走系統代理的,需要額外設置?!疚⑿砰_發者工具-》設置-》代理-》指向代理服務器】
利用反向代理實現跨域
反向代理需要用到nginx,其詳細介紹請看 http://www.nginx.cn/doc/http://www.nginx.cn/doc/
實現原理
原理大體相同,但是處理的端不同,反向代理實在服務器端進行處理。首先修改hosts文件,將域名指向開發者的電腦本身,
把自己偽裝成服務端,再通過nginx對不同的請求進行轉發,把靜態資源指向開發者本地電腦的資源,將接口指向實際的服務器。
相當于把飯店設置在了黑社會的樓下,去樓下買醬排骨飯的時候,飯店飯自己做,醬排骨則偷偷跑去別的飯店買。
代理配置
- 設置hosts文件,將目標域名指向本機。
- 編輯nginx配置,對不同的資源請求,指向到對應地址。同樣的,將靜態資源指向本機服務,將接口指向真正的服務器。
程序運行過程
- 瀏覽器訪問頁面,假設訪問淘寶頁面:taobao.com/index.html
- taobao.com域名解析先經過hosts文件配置,發現taobao.com域名指向127.0.0.1,則向本機發起請求。
- nginx接收到taobao.com/index.html請求,根據nginx的配置,將把這個請求轉發給127.0.0.1:3000。
- 瀏覽器拿到index.html文件,發起taobao.com/api/getNew請求
- nginx接收到taobao.com/api/getNew請求請求,根據nginx的配置,將把這個請求轉發給真正的淘寶服務器中。
- 淘寶服務器將數據返回給nginx,再返回給瀏覽器執行。
簡單的對比
- 使用charles等正向代理方式比較簡單,需要掌握的知識點也比較少。但同時其可配置性較弱,更適合小型項目使用。
- 使用nginx的反向代理則相對復雜一些,需要了解基本的nginx配置。但其可配置性較強,支持URL的正則匹配,設置優先級等,適合復雜的項目使用