Service Mesh 架構下的認證與授權

認證與授權幾乎是所有系統必不可少要處理的問題。在傳統架構下,我們習慣了在程序中寫一些代碼或引一些類庫來處理其相關的邏輯,但如果在 Service Mesh 架構下,會有什麼不同?

Service Mesh 的核心是將一切非業務功能交給基礎設施層,討論 Service Mesh 架構下的認證與授權,實質上是討論能否將認證與授權的處理邏輯委託給基礎設施層,從而讓應用層更加專注於業務。

爲了能讓我們的討論更加形象化,我們可以藉助一個案例。

背景

某組織要舉辦一次籃球相關的活動,要求參與者以團體的方式報名。

參賽人員通過微信賬號登錄系統並驗證手機號後,填寫個人信息及團隊其它成員的手機號與信息並報名活動,其它成員通過微信賬號登錄並驗證手機號後,可以看到與自己關聯的活動申請。

組織方工作人員同樣通過微信登錄並驗證手機號,之後可以獲取其所負責區域的活動申請並做審覈操作。

系統結構

我們需要一個用戶池來管理用戶,但我們不想在用戶的賬號密碼等邏輯上花費太多精力,因此決定使用微信作爲我們的用戶池,用戶進入我們的系統時,使用微信登錄即可,這樣就可以將 “創建新用戶”,“修改密碼”,“找回密碼” 和“短信驗證碼”等邏輯委託給微信,同時也免去了用戶進入系統時的 “註冊” 過程,一舉兩得。

接下來,我們需要一個維護活動申請的服務,但因爲活動申請需要的信息較多,用戶可能無法一口氣完成,所以還需要一個能幫助用戶分步驟創建活動申請的服務。

最後,活動組織方需要審覈活動申請,但活動組織方的管理人員也是用微信登錄系統,那麼我們的系統就得有能力知道當前用戶是不是工作人員,因此我們需要一個領域專屬用戶信息服務。

以下是一幅簡單的系統邏輯結構圖:

備註 1:此圖隱去了其它相關部分,以免結構圖變得複雜,導致討論範圍擴散。

認證

在這個案例中,有兩類具有不同數據訪問權限的用戶,爲了能知道當前訪問系統的用戶歸屬於哪一類,那麼系統必須得先知道當前用戶是誰,這樣才能找出用戶所具有的授權。認證是授權的先決條件,識別出當前用戶是誰,就是 “認證”。認證大體上分爲“系統識別用戶” 和系統內 “微服務識別用戶”,“微服務識別用戶” 是我們的重點。

系統識別用戶

最終處理用戶請求的是系統中的各個微服務,但我們不能讓每個微服務都和登錄流程打交道,所以需要在系統將請求轉發給微服務前,完成對用戶的識別。

登錄

用戶要使用我們的系統,首先要去登錄界面,登錄界面引導用戶使用微信登錄。微信做爲 User Pool 幫我們完成對用戶的認證,然後我們的系統會給用戶發一個 “外部通行證”,通常情況下,“外部通行證” 是一個隨機字符串,放在 Cookie 中。之後用戶向系統發起請求時,便不再需要提供賬號密碼,只要帶上個這個 “外部通行證” 即可。

訪問系統

用戶向系統發起請求時,需要在請求中附上 “外部通行證”,這樣系統就能知道請求來自於誰。如果請求中沒有“外部通行證” 或“外部通行證”不合法,則要求用戶重新登錄。如果 “外部通行證” 合法,系統則通過 “外部通行證”,找到用戶在系統中的記錄及其關聯的授權信息,然後生成一個“內部通行證”。“內部通行證” 通常情況下是 JWT,包含更加豐富的信息,並對用戶不可見,系統內微服務只關心“內部通行證”。

微服務識別用戶

用戶的請求最終需要訪問系統內的微服務,爲確保到達微服務的請求是由合法的用戶發起的,而不是系統內被劫持的其它子系統捏造的,微服務仍然需要驗證請求者的身份。微服務驗證請求者身份的方式,就是驗證請求中附帶的 “內部通行證” 是否合法。

在被請求的微服務處理請求前,需要先驗證 “內部通行證” 的合法性,也就是驗證請求中附帶的 JWT。驗證 JWT 需要先獲取 Key(加密方式決定 Key 的類型),之後用這個 Key 計算 JWT 是否被篡改過。案例使用的 JWT,但驗證內部通行證的主流方式都不涉及被請求服務的業務。這個過程與微服務自身承載的業務完全沒有關係,但它又是微服務不可或缺的一部分。傳統方式下,我們會把這部分邏輯放在微服務中,並讓這部分邏輯獨立於微服務的業務模塊,但微服務的職責範圍還是擴大了,導致構建微服務時,開發人員的關注範圍要擴大。如果把這部分邏輯交給 Sidecar,我們的微服務可以更加純淨,開發人員在構建微服務時也可以更加聚焦。

授權

當請求者的身份被識別後,下一步要做的就是檢查請求者是否被授權訪問其請求的內容。

用戶到服務

用戶需要向 Application Service 提交活動申請,那麼用戶就得有對 Application Service 的訪問權限。

用戶的請求到達後,首先檢查 “內部通行證”,確保請求是由合法的用戶發起的。之後檢查發起請求的用戶是否有權限訪問這個服務或這個服務的某一個 API,有多種方式可以做,案例中的方式爲檢查“內部通行證” 中對這個 API 訪問權的描述。這個過程與微服務自身承載的業務完全沒有關係,與前面 “認證” 中“微服務識別用戶”的情況相同。

服務到服務

用戶發起的請求,可能系統內一個微服務就可以完全處理,也有可能需要多個微服務協同處理。所以對於微服務,請求者可以是用戶,也可以是其它微服務。在我們這個系統中,微服務之間會有交互,但它們之間的訪問性不是無限制的。Application Service 在接到用戶提交的活動申請時,需要訪問 Domain Specific User Information Service 獲取一些數據,用來構建活動申請。對於 Domain Specific User Information Service,來自 Application Service 的請求,應予以處理,但如果請求來自其它服務,則要拒絕。

服務間的訪問性配置需由另外的服務提供,做這步檢查時需要先獲取服務間的訪問性配置,然後依據配置拒絕或放行請求。這個過程與微服務自身承載的業務完全沒有關係,同樣與前面 “認證” 中“微服務識別用戶”的情況相同。

用戶到服務的特定數據

一個活動申請包含多位參賽人員及他們的手機號與身份證號,當團隊中一位成員代表團隊提交了活動申請,我們需要讓團隊其他成員在完成認證後,也能看到這份活動申請及其狀態。這種情況下,活動申請是否可被訪問的 “配置” 包含在活動申請自身中,其它服務無法提供。也就是說,只有 Application Service 自身才能提供活動申請能否被訪問的“配置”。

這個 Sidecar 在做數據訪問性檢查時,要請求微服務來獲取數據,用於判斷請求是否被授權。這個提供信息用於做訪問檢查的 API 是被做 “數據訪問性檢查” 的 Sidecar“驅動”出來的,那麼本質上,微服務參與了權限驗證。

因爲負責數據授權檢查的 Sidecar 需要從微服務獲取數據才能完成工作,所以微服務需要對做數據授權檢查的 Sidecar 提供可獲取授權信息的 API,這導致授權檢查的職責沒能被完全委託出去。

對於很多使用關係型數據庫的微服務來說,可能只是 SQL 中 INNER JOIN 的過濾,無需額外做任何事情。如果需要過濾從數據庫中拿到的數據,服務內數據通信的便捷性強於服務間數據通信。

剛纔我們提到,只有 Application Service 自身才能提供活動申請能否被訪問的 “配置”,這其實已經說明了對於這種場景,維護這份 “配置”,就是 Application Service 的業務。因此,這種場景讓微服務自己做,能讓邏輯更加內聚,也能減少不必要的工作。

由於活動在 10 個城市中舉辦,現有 5 個活動管理員,平均每人管理兩個城市的活動申請,不同管理員之間不共享數據。

我們有城市 1 - 10,其中 1 和 2 歸屬管理員 1,當管理員 1 要獲取他所負責的所有活動申請時,需要用到類似下面的接口。GET /applications?cities=1,2

他可能也只想查看城市 1 最近幾天收到的申請。GET/applications?cities=1&submittedAfter=xxxx-xx-xx

但他不能查看城市 1 或 2 以外任何城市的申請,下面的請求應該被直接拒絕。GET /applications?cities=3,6

因爲數據的訪問性配置是由其它服務提供的,微服務不需要也沒有能力向這個 Sidecar 提供支持,也不需要知道有這個 Sidecar。但負責數據授權檢查的 Sidecar 需要學習微服務的業務,並且這部分內容無法複用。

雖然這個 Sidecar 要學習微服務的業務,成爲這個服務專有的 Sidecar,但是從微服務的角度看,數據授權檢查的邏輯被完全委託出去了,微服務可以更加純淨,開發人員在構建微服務時也可以更加聚焦。

總結

在微服務處理請求前,對請求者身份的驗證也就是對認證信息的檢查,應委託給基礎設施層。

授權配置在微服務之外時,如果是 “服務到服務” 和“用戶到服務”的場景,應委託給基礎設施層,如果是“用戶到服務的特定數據”,類似按城市分配管理員審覈活動申請的場景,可優先考慮委託給基礎設施層。

授權配置在微服務之內時,如果是 “用戶到服務的特定數據”,類似參賽人員與活動申請的關係,屬於微服務本身需要維護的業務邏輯,應優先考慮服務內實現。


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。