首頁 >Java >java教程 >一文詳解怎麼實現微服務鑑權

一文詳解怎麼實現微服務鑑權

藏色散人
藏色散人轉載
2023-01-27 08:30:022300瀏覽

本篇文章為大家帶來了關於鑑權的相關知識,其中主要介紹了微服務中鑑權的實現思路是什麼?又有什麼好的方案可以實現呢?下面一起來看一下,希望對大家有幫助。

最近剛好有小夥伴在微信上問到這個問題,我就來和大家聊一聊,本文主要和小伙伴們聊一聊思路,不寫代碼,小伙伴們可以結合我之前的文章,應該可以自己寫出來本文的程式碼。當然,想法也只是我自己的一點實務經驗,不一定是最完美的方案,歡迎小夥伴們在留言中一起探討。

1. 認證與授權

首先小夥伴們知道,無論我們學習Shiro 或Spring Security,裡邊的功能無論有哪些,核心都是兩個:

  • 認證

  • 授權

所以,我們在微服務中處理鑑權問題,也可以從這兩個方面來考慮。

1.1 認證

認證,說白了就是登入。傳統的 Web 登入是 Cookie Session 的方案,這種方案依賴伺服器本地內存,在微服務中,由於服務眾多,這種方案顯然不再合適。

可能會有小夥伴說用 Redis SpringSession 做 Session 共享,這是個辦法,但是不是最佳方案,因為這種方案的性能以及可擴展性都比較差。

所以,微服務中的認證,還是建議使用令牌的方式,可以選擇 JWT 令牌,這也是目前使用較多的一種方案。但熟悉JWT 的小夥伴都知道,純粹的無狀態登入無法實現註銷,這就很頭大,所以在實際應用中,單純的使用JWT 是不行的,一般還是要結合Redis 一起,將生成的JWT 字符串在Redis 上也保存一份,並設定過期時間,判斷用戶是否登入時,需要先去Redis 上查看JWT 字串是否存在,存在的話再對JWT 字串做解析操作,如果能成功解析,就沒問題,如果不能成功解析,就表示令牌不合法。

這樣有狀態登入 無狀態登入混在一起的方式,雖然看起來有點不倫不類,但是就當下來說,這個折衷的辦法算是一個可行的方案了。

其實,上面的方案,說穿了,跟傳統的Cookie Session 沒什麼兩樣,思路幾乎都是完全copy 的:傳統的Session 用Redis 代替了;傳統穿梭於服務端和瀏覽器之間的jsessionId 被JWT 字串取代了;傳統的jsessionId 透過Cookie 來傳輸,現在的JWT 則透過開發者手動設定後透過請求頭來傳輸;傳統的Session 可以自動續簽,現在用JWT 就是手動續簽,每次請求到達服務端的時候,就去看下Redis 上令牌的過期時間,快過期了,就重新設定一下,其他都一模一樣。

這是認證方案的選擇。

1.2 授權

微服務中授權,也可以使用 Shiro 或 Spring Security 框架來做,省事一些。考慮到微服務技術堆疊都是Spring 家族的產品,所以在權限框架這塊也是建議大家首選Spring Security(如果有小夥伴對Spring Security 還不熟悉的話,可以在微信公眾號後台回复ss,有教程) 。

當然,如果覺得 Spring Security 比較複雜想自己搞的話,也是可以的。自己搞的話,也是可以藉助於Spring Security 的思路的,松哥最近的一個項目就是這樣:

請求到達微服務之後,先找到當前用戶的各種信息,包括當前用戶所擁有的角色和權限等訊息,然後存入到和當前線程綁定的ThreadLocal 物件中。另一方面自訂權限註解和角色註解,在切面中對這些註解進行解析,檢查目前使用者是否具備所需的角色/權限等。

當然,如果你使用了Spring Security 的話,上面這個就不需要自訂註解了,直接使用Spring Security 中自帶的即可,還可以體驗Spring Security 中更多的豐富的安全功能。

2. 認證服務

那麼認證和授權在哪裡做?

先來說認證,認證我們可以簡單分成兩個步驟:

  • 登入

#2.1 登入

一般來說,登入我們可以單獨做一個認證服務。 當登入要求到達網關之後,我們將其轉送到認證服務上

,完成認證操作。

在認證服務上,我們就去檢查使用者名稱/密碼是否OK,使用者狀態是否都OK,都沒問題的話,產生JWT 字串,同時再把資料存入Redis 上,然後把JWT 字串傳回。

如果系統有註冊功能的話,註冊功能也是放在這個微服務上來完成。

2.2 校驗

校驗是指每個請求到達的時候,校驗使用者是否已經登入。

這當然可以跟 2.1 放到一起去做,但松哥不建議。問題在於,假如是一個創建訂單的請求,這個請求原本是要經過網關轉發到訂單服務上的,但是,此時就得先在網關上調用2.1 小節的服務進行登錄校驗,沒問題再轉發到訂單服務上,這樣做很明顯很費事,也不合理。

一個比較好的方法是直接在網關上去校驗請求的令牌是否合法,這個校驗本身也比較容易,校驗令牌是否合法,我們只需要看Redis 上是否存在這個令牌,而這個JWT 令牌能夠被順利解析就行,這個操作完全可以在網關上做。

以 Gateway 網關為例,我們可以自訂全域過濾器,在全域過濾器中校驗每一個請求的令牌,校驗通過了,再進行請求的轉發,否則就不轉發。

校驗通過之後,在轉發到具體的微服務之後,我們可以將解析出來的用戶id 以及用戶名等資訊放到請求頭中,然後再轉發,這樣到達各個具體的微服務之後,就知道這個請求是誰發來的,這人都有哪些角色/權限,方便做下一步的權限校驗。

松哥的做法是定義了一個公共模組,所有的微服務都依賴這個公共模組,這個公共模組中定義了一個攔截器,會攔截下來每一個請求,從請求頭中取出用戶ID,然後從Redis 中拿到具體的用戶信息,存入到ThreadLocal 中,這樣在後續的方法調用中,如果需要判斷用戶是否具備某一個權限,就可以通過ThreadLocal 去獲取了。

大致上就是這樣一個流程。

3. 授權服務

授權沒辦法放到網關上做,還是得在各個微服務上去完成。

微服務上的授權我們又可以將之大致上分為兩類:
  • 前端發送來的請求(外部請求)。 ###
  • 別的微服務發送來的請求(內部請求)。

3.1 外部請求

對於外部請求來說,就按正常的權限校驗對待就行了,自訂註解亦或者使用Spring Security 等框架都是可以的,如果是自訂註解的話,就結合AOP 一起,定義切面自己去處理權限註解,當然,這些功能基本上每一個微服務都是需要的,所以可以將之抽取成為一個公共的模組,在不同的微服務中依賴即可。

3.2 內部請求

對於內部的請求來說,正常是不需要鑑權的,內部請求可以直接處理。問題是如果使用了OpenFeign,資料都是透過接口暴露出去的,不鑑權的話,又會擔心從外部來的請求調用這個接口,對於這個問題,我們也可以自定義註解AOP,然後在內部請求調用的時候,額外加一個頭字段加以區分。

當然,內部請求到達微服務的時候,也是需要進行認證的,就行請求從網關轉發到每一個具體的微服務上時需要認證一樣,不過很明顯,我們沒必要每次使用OpenFeign 呼叫別的服務的時候,都會傳一堆認證訊息,我們可以透過實作feign.RequestInterceptor 介面來定義一個OpenFeign 的請求攔截器,在攔截器中,統一為OpenFeign 請求設定請求頭資訊。

好啦,關於微服務中的鑑權,我們目前是這麼做的,歡迎小夥伴們留言一起探討。

推薦學習:《小程式影片教學》《Java影片教學

#

以上是一文詳解怎麼實現微服務鑑權的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除