微服務 Token 鑑權設計的幾種方案

Token 透傳(不推薦)

剛開始接觸微服務時網上給的方案大都數是通過透傳 Token 做鑑權,但我認爲這種方式不是很妥當。接着往下看:

這種方式通過透傳 Token 使得各微服務都能獲取到當前登錄人信息,在代碼編寫上確實可能會方便,但我認爲這不是一種很好的設計方式。

原因一:內部 API 與外部 API 混合在一起不太好區分。

原因二:內部調用的微服務 API 因該具備無狀態性質,這樣才能保證方法的原子性以提高代碼複用率。

換句話說:B 服務提供 API 時不因該關心當前是否爲登錄狀態,登錄狀態應該由路由中的第一個服務校驗維護,在調用後續服務時應該顯示的傳入相關參數。比如以下場景:

場景一:用戶簽到添加積分

場景二:後臺管理員給用戶手動添加積分

場景三:分佈式調度批量增加用戶積分

根據需求積分服務提供了一個給用戶添加積分的 API,如果你的 API 是通過獲取的當前登錄用戶 ID 增加的積分,那麼面對場景二時你需要重新編寫一個給用戶添加積分的 API,因爲當前登錄的是後臺管理員而不是用戶(代碼複用率較低)

不透傳數據,顯示的提供入參

路由到達的第一個服務已經對 Token 進行了解析認證並將 userId 顯示的傳遞給了後續服務,後續服務不需要再對 token 進行解析認證。根據 1.1 的三個場景只需要提供一個入參包含 userId 的 API,保證了函數的原子性提供代碼複用率。

注意: 提供的 API 不能暴露給外網,我們需要在路徑上做區分,避免外網非法訪問內部 API。我們可以訂好內部調用 API 路徑規則,如: /api/inside/\*\* 。在網關層拒絕內部調用 API 請求的訪問。

統一授權

統一授權是指:將 API 鑑權集中在應用網關上

Fegin 內部調用方式

Spring Cloud Gateway + Fegin 內部調用,集中在 Gateway 上做統一認證鑑權,鑑權後在請求頭中添加鑑權後的信息轉發給後續服務,如:userId 等。。。

缺點:A 服務調用 B 服務時,B 服務需要寫一個內部調用的 Controller 接口 A 服務才能通過 Fegin 調用到 B 服務,增加了代碼量(這裏的設計方案是內部調用與外部調用 Controller 是分開的)

Dubbo 內部調用方式

Spring Cloud Gateway + Dubbo 內部調用,集中在 Gateway 上做統一認證鑑權,鑑權後在請求頭中添加鑑權後的信息轉發給後續服務,如:userId 等。。。

優點:與第一種相比不需要額外編寫一個 Controller 接口,只有本地 service 與遠程 DubboService 的區別,代碼更簡潔。

缺點:項目技術棧略微增加了複雜度。

Spring Boot Web + Dubbo 內部調用方式

這裏的設計方案直接去掉了 Gateway,直接使用了一個 Spring Boot Web 項目來代替 Gateway。但需要注意的是應該將 Web 項目的容器換成 Undertow,因爲 Tomcat 是阻塞式的容器,不換也不是不行,但吞吐量可能會少一下,Undertow 是非阻塞式的容器,可以與 Gateway 到達相同的效果。(非阻塞式:當請求爲線程進入阻塞狀態時,當前線程會被掛起,當前的計算資源會去做別的事情,當被掛起的線程收到響應時纔會被繼續執行,壓榨 CPU 用更少的資源做更多的事情,但並不會提升性能)

因爲去掉了 Gateway 我們需要將所有服務的 Controller 集成到 Web 應用,然後在這個 Web 應用上做統一認證授權。如果將所有代碼寫到 Web 應用中,這樣可能不合適,我們可以選擇每個服務創建一個 Controller 模塊,Web 網關服務只有一個啓動類,通過依賴的方式集成所有服務的 Controller。

優點:簡化了項目結構,所有服務只有 service 代碼。性能壓測時不用考慮 Gateway 的線程池使用情況,業務服務只需要考慮 Dubbo 線程池的使用情況。

缺點:沒辦法通過配置中心動態調整路由。比如說增加了一個服務 Gateway 可以不重啓通過配置中心增加路由配置即可。

非統一授權

非統一授權:不在應用網關上集成鑑權,網關只有單一的路由轉發業務。各位服務都有自己的鑑權方式,當然也可以通過 jar 包的方式統一各服務的鑑權方式。

常規模式

通過編寫通用的鑑權模塊,各服務集成該模塊。該模塊具備以下功能:

  1. JWT Token 解析

  2. 權限校驗攔截

  3. 緩存(本地緩存 \ Redis 緩存)

這種模式更適合大型項目團隊,可能各微服務都由一個項目組負責。各服務維護自己的權限規則(這裏指的是權限規則數據,規則是統一的)

該模式下由於應用網關比較輕量級,不再涉及複雜的鑑權流程,使得項目部署可以更靈活,當我們使用 K8S 部署項目時,我們可以將應用網關替換成 K8S 中的 Ingress 網關。

我們先看常規模式部署在 K8S 中完整的鏈路:

當用戶訪問時會先到達 K8S Ingress 網關通過應用網關 Service 的負載均衡調用應用網關,應用網關需要通過註冊中心獲取服務註冊列表,通過服務註冊列表負載均衡到後續服務。

與 K8S 集成

我們再來看看將應用網關替換成 K8S 中的 Ingress 網關的完整鏈路:

這裏不僅只是去掉了應用網關,同時我們通過 K8S Service 負載均衡的能力去掉了註冊中心。減少了我們部署微服務時還要額外搭建一套註冊中心。同時減少了一層沒必要的轉發。至於 K8S 中的 Service,大家可以理解成一個本地的 host 假域名,比如我們在 K8S 給商品創建一個 Service,名稱爲:goods-svc。那麼我們可以通過 goods-svc 直連。如:

  1. http://goods-svc:8080/api/goods/info/10001

  2. dubbo://goods-svc:20880

方案沒有對錯,選擇適合自己的就是最好的。

來源:juejin.cn/post/7329352197837029385

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eh_J69N75IgtAYNR-_So2A