基於springmvc的鑒權,遷移到springcloudgateway的設計


gateway重構

背景

當前需要把統一鑒權的業務流程提取到網關,做統一的認證,這樣各個服務不在使用spring mvc在攔截器中進行處理。雖然以往的項目中基於servlet提取了公共的組件,但每次升級,各個服務都需要升級jar包,而且鑒權類的工作細化到具體服務確實不恰當。

服務流程設計

首先一個請求先到達gateway然后從gateway中獲得鑒權結果,最后得出鑒權失敗直接響應客戶端,或鑒權成功直接轉發到具體服務后將響應結果原路返回給客戶端。

        

網關詳細設計

網關平台技術組件選擇了spring gateway(后面簡稱gateway),目前根據請求的url信息,去進行gateway predicte path的驗證,如果路由可以匹配,則進行filter過濾鏈中的鑒權處理。

之前版本的鑒權是基於spring mvc的攔截器的,而且在鑒權流程中完全依賴於HttpServletRequest和HttpServletResponse兩個組件。所以核心業務流程的代碼可復用性不高,而部分的util工具類中有些方法是可以進行服用的,目前階段可以直接引用老版本的jar包來進行使用;這樣能夠將部分輔助業務邏輯保留。

本次使用gateway組件,而鑒權階段可能涉及多個步驟,如:1准備上下文,2 收集LoginType,SiteID信息  3 驗證passport等。。。

目前多個步驟可以散落在gateway的不同filter中,也可以自己封裝核心鑒權流程業務到同一個filter中,而不同的filter用來划分請求和響應環境的不同功能,比如除了鑒權之外的負載均衡,以及限流等其它業務。

而且目前參考老版本基於攔截器的實現,覺得很有必要將核心業務邏輯分離,本次的研發將不在完全依賴於gateway,以便於后期改用其它基礎平台框架,而導致業務代碼耦合性增大。

  • 分離鑒權業務邏輯以及gateway之間的耦合

首先從老版本的servlt強依賴入手,那么話說回來,gateway中使用netty作為底層的通訊組件,spring-webflux作為web項目的基礎,那么gateway中使用ServerWebExchange作為請求響應信息的承載對象,那么在這里我們需要進行封裝;避免日后不在使用geteway時發生類似的問題,導致沒法和框架的通訊對象解綁。

在這里我們抽象出RequestInfo以及Response對象為約定規范,而對應不同平台的請求對象和響應對象只需要進行一次包裝即可,實現接口約定中的方法;這樣我們在使用到具體的業務流程類中可以使用我們約定接口的包裝對象,這樣即使后面更換基礎平台,再次遇到基礎組件被更換時,由於我們沒有強依賴僅僅需要書寫對應的wrap即可,不需要大刀闊斧改造核心業務類。

 

  • 自定義gateway的route定義流程

基於gateway默認提供的RouteDefinitionRepository接口,去自定義getRouteDefinitions方法,這樣通過發布RefreshRoutesEvent事件即可更新本地路由;gateway會去自主調用getRouteDefinitions方法返回多個RouteDefinitions,然后通過gateway內部提供的RouteDefinitionRouteLocator類中的convert方法轉換成Route對象,在這個過程中gateway實際是提供了一種針對於route的map緩存機制,這樣我們可以根據需要,定期通過發布事件去刷新緩存獲得最新的route信息列表,當然route信息可以來自於redis,mysql等第三方存儲介質。

 

 

 

上面的緩存機制源於CachingRouteLocator這個類。該類的定義如下:

 

而每一次請求都會調用CachingRouteLocator的getRoutes方法,根據請求信息去Map緩存列表中,匹配已經緩存的Route,匹配成功后進行后續處理;

 

那么上面的@Bean定義部分我們可以看到,這個RouteLocator接口實現,我們是可以自己去設計的,可以按你的方式自定義這個getRoutes的環節。

那么在獲取路由列表時也提供了公共的接口規范,這樣與實際的route的存儲容器松耦合。

 

  • gateway整體的運行流程

目前網關中的核心類如下:

 

網關的主體流程分幾個部分:

  1. gateway請求上下文載體ServerWebExchange
  2. 鑒權流程數據傳遞類ContextBridge
  3. 登錄時的ads請求信息載體LoginContext

在gateway匹配到路由信息以后,請求轉到filter中,首先我們收集請求中的基本信息;封裝LoginContext中的對象中,將相關的對象在ContextBridge中持有引用。

 

這三個方法是匹配路由成功以后必須執行的,如果鑒權成功以后,后面的服務是要獲取LoginContext的。

除了上面的3個方法以外,后面還會加入siteid以及loginType等信息的獲取。

上面提到的都是登錄信息的收集,那么實際的鑒權流程核心在AuthenticationConfiguration配置類中,該類會將繼承PlatformCheck抽象類,所有bean收集到List中,然后根據接口中約定getOrder方法排序執行,鑒權邏輯,在每一個鑒權的主題中關注當前的業務邏輯,實現不同鑒權階段的拆分,如果鑒權失敗可以直接返回false,並調用方法:

ResponseUtil.buildResponseFinish(responseInfo,InterceptorUtils.ErrorResult(-2002, "pc_passport認證失敗,檢查cookie信息thor"));

       進行gateway直接響應的設置,那么最后鑒權階段的filter中我們就可以通過承載對象ContextBridge獲取到已經設置好的responseInfo,並直接響應給客戶端。

       Ps:下面的核心流程中解決了一個重要問題,就是在原始的攔截器流程中,存在兩種流程:

1 可能直接結束掉流程的代碼;

2 一旦進入某個if分支后,一定會結束掉流程的代碼;

那么基於當前的場景,我們在統一驗證流程的基類中添加3個方法,check方法 skipExecute方法 execute方法;如果存在可能執行結束的邏輯時,將業務的實現寫在check方法中,這時skipExcute方法可以設置為true,那么execute方法始終被跳過,此時check邏輯返回為false時則流程結束,check返回為true時流程繼續。

如果遇到進入if分支邏輯以后一定會結束掉流程的場景,那么在check方法始終返回true即可,然后if如果成立則skipExecute返回false,證明execute一定會執行並返回最終結果結束掉流程,后面的所有業務實體將不在繼續流轉。如:下圖step3。

 

       目前gateway中passport認證類已經做好,后面有其它的鑒權步驟,可以模仿PassportPlatformCheck實現類繼承PlatformCheck抽象類來做,這樣暫時不用關注gatewayFilter的其它核心流程。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM