文章版權由作者李曉暉和博客園共有,若轉載請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/
1.背景描述
跨域問題是瀏覽器同源安全制引起的特別常見的問題。不同前端語言針對跨域解決方法有所區別。比如Flex語言做跨域請求時,如果中間件存有跨域文件(crossdomain.xml)則可以輕松實現跨域。
而JS開發的前端,針對GET請求則又可以通過JSONP方式解決。補充一下JSONP的原理:通過創建一個 script 標簽,將 src 設置為目標請求,插入到 DOM中,服務器接受該請求並返回數據,數據通常被包裹在回調鈎子中。根據JSONP的實現原理,我們可以看到其無法支持POST請求方式。
回到我們文章的主題,使用tomcat發布了Geoserver,前端JS腳本通過常規JSONP的請求方式失效,此時如何實現跨域請求呢?
這里我總結兩種思路,一種是轉發代理方案,另一種是中間件支持跨域共享機制(CORS)。
2.方案一:轉發代理
2.1基於Nginx的代理
假設有A服務器和B服務器,用戶前端加載了A服務器的JS資源,然后該JS向B服務器后台發送請求,此時發生了跨域。基於Nginx的解決方案則是,在C(或者A或者B或者其他)服務器上搭建一個Nginx服務器,該服務器對A服務和B服務均做統一端口的代理,即前端通過同一IP:Port訪問A上和B上的資源,從而避免瀏覽器的跨域問題。
方案示意圖為:
2.2自定義實現代理轉發(基於A服務器)
2.2.1原理
目前A服務器上的腳本想訪問B服務器上的服務會引發跨域,如果我們將A服務器的腳本訪問轉移到A服務器上的后台對B服務器后台的訪問,則可以規避跨域問題。
方案示意圖為:
2.2.2具體實現
如果我們的代碼層面僅僅是單獨針對某個請求讓其轉移至后台,則其他類似問題則無法通用此解決方案。這里我們可以設計一種通用型的解決方案:
a.將所有需要走后台避免跨域的請求統一定義為http://IP:Port/name/proxy?Url=BServerUrl。
b.后台對應的proxy方法中獲取到Url后的參數,並且再次對Url后的參數進行傳遞參數的解析(BServerUrl中可以用&包含正常參數)。
c.轉發解析到的B請求,獲取返回結果再返回至前端。
注意,在實現中,我們還要同時考慮代理轉發的Get請求和Post請求方式:
3.方案二:中間件跨域共享機制(CORS)
3.1CORS原理簡介
出於安全原因,瀏覽器限制從腳本內發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味着使用這些API的Web應用程序只能從加載應用程序的同一個域請求HTTP資源,除非使用CORS頭文件。
CORS的原理為:跨域資源共享標准新增了一組 HTTP 首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。另外,規范要求,對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。
以上內容均摘抄於:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS。
3.2具體實現
先下載CORS對應的Jar:
<!-- cors-filter-->
<dependency org="com.thetransactioncompany" name="java-property-utils" rev="1.10" conf="common-release->default;" />
<dependency org="com.thetransactioncompany" name="cors-filter" rev="2.5" conf="common-release->default;"/>
在Geoserver的Web.xml中加上如下配置:
重啟Geoserver后測試。
測試環境說明:在同一台機器上測試,系統采用8081端口,Geoserver服務采用8080端口,不同端口同樣會導致跨域。測試結果為跨域成功。