詳解微服務中的三種授權模式

作者 | Graham Kaemmer

譯者 | Rayden

審校 | 王強

在過去的 5 個月裏,我與 50 多家公司討論了他們的授權系統。其中超過一半的公司以某種形式使用微服務,我對它們帶來的授權挑戰非常感興趣。在面向服務的後端進行授權這一問題上,似乎沒有公認的最佳實踐。我與很多團隊進行了交談,有的團隊將用戶角色附加到身份驗證令牌上,有的將所有內容存儲在專門用於授權的圖數據庫中,還有的團隊在 Kubernetes 邊車(sidecars)中自動執行授權檢查。這些解決方案中都沉澱了數月或數年的工程工作,每個團隊都發明瞭自己的輪子。這是爲什麼?

當你有一個單體應用時,你通常只需要訪問一個數據庫來決定是否允許用戶做某些事情。單體應用的授權策略本身不需要太關心在哪裏找到數據(比如用戶角色)——你可以假設所有數據都可用,如果需要加載額外數據,它也可以很容易地從單體應用數據庫中獲取。

但是這個問題在分佈式架構中變得困難了許多。也許你正在將單體應用拆分爲多個微服務,或者你正在開發一個新的計算密集型的服務,在運行作業之前需要檢查用戶權限。現在,決定誰可以做什麼的數據可能不那麼容易獲取。你需要新的 api,以便你的服務能夠相互談論權限:“誰是這個組織的管理員?誰可以編輯這個文檔?他們可以編輯哪些文檔?” 爲了在服務 A 中做出決策,我們需要服務 B 中的數據,服務 A 的開發人員如何請求這些數據?服務 B 的開發人員如何使這些數據可用?

這些問題有很多答案,所以我試圖將這些答案歸納爲幾個廣泛的模式。這些模式不一定能覆蓋所有解決方案(解決方案的世界很複雜),但我發現它們能幫助我與不同的人談論他們所構建的東西。當我與一個新團隊進行對話時,它們讓我更容易對解決方案進行分類。

在構建微服務時,我看到了處理授權數據的三種主要模式。我將在這篇文章中討論這三種方法:

  1. 將數據留在原處,讓服務直接請求它。

  2. 使用網關將數據附加到所有請求,以使其隨處可用。

  3. 將授權數據集中到一個地方,並將所有決策轉移到那個地方。

1 爲什麼微服務中的授權更困難?

讓我們以某個授權場景爲例,這是一個用於編輯文檔的應用程序。它很簡單,但應該能說明問題:

在一個單體應用中,用一種清晰的方式表達這種邏輯並不太難。當你需要檢查用戶是否可以閱讀文檔時,你可以檢查該文檔屬於哪個組織,加載該組織中用戶的角色,並檢查該角色是成員還是管理員。這些檢查可能需要額外的一兩行 SQL 語句,但數據都在一個地方。

當你將應用程序拆分爲不同的服務時,會發生什麼情況?也許你已經剝離了一個新的 “文檔服務”——現在,檢查特定文檔的讀權限需要檢查位於該服務數據庫之外的用戶角色。文檔服務如何訪問它所需要的角色數據?

2 模式 1:將數據留在原處

通常,最簡單的解決方案是將數據留在原處,並讓服務在需要時請求它所需要的數據。對於上述問題,你可能認爲這是最明顯的解決方案。

你可以將數據模型和邏輯分開,這樣文檔服務就可以控制向哪個角色授予哪些文檔相關的權限(管理員可以編輯,成員可以讀取,等等),然後用戶服務公開一個 API 來獲取組織中用戶的角色。有了這個 API,權限檢查可以像這樣進行:

有一個合理的論點認爲最簡單的解決方案就是最好的方案,在這裏它通常是沒問題的。根據我的經驗,這通常是當團隊開始轉向微服務並想讓用戶授權正常工作的情況下所使用的解決方案。它完成了工作,而且不需要任何額外的基礎設施。

當服務或團隊數量增加、授權邏輯變得更復雜或面臨更嚴格的性能要求時,此模式開始出現問題。要讓該模式正常工作,任何新服務的開發人員都需要知道如何從用戶服務中獲取角色數據,而用戶服務本身必須擴展以滿足這種需求。隨着服務依賴關係的增加,該模式可能會增加不可預測的延遲和重複請求。也許引入一個單獨的 “文件夾” 服務就會導致系統需要通過服務之間的相互調用來進行權限檢查:

儘管有變得混亂的風險,但這種模式可以讓你走得很遠。無需部署和維護額外的授權基礎設施可能是一個巨大的優勢,如果具有數據的服務能夠處理來自需要數據的服務的負載,那麼將它們串在一起就是一個很好的解決方案。

有一些團隊遵循這種通用模式,但他們認爲應該用某種專門的授權服務替換所有這些請求流,我和這些團隊有過交談。我總是問他們真正的問題是什麼。如果問題是時延,也許在正確的位置添加緩存可以解決這個問題。如果授權邏輯在服務中變得越來越混亂,那麼可能需要強制採用標準策略格式。(Oso 是一個解決方案;還有其他解決方案。)

但是,如果問題是你的數據模型變得過於複雜,或者你在重複實現相同的 api,或者權限檢查需要與太多不同的服務通信,那麼也許是時候重新考慮架構了。

3 模式 2:請求網關

解決授權數據問題的一個優雅的解決方案是將用戶角色包含在對服務(這些服務可能需要做出授權決策)的請求中。如果文檔服務在請求中獲得有關於用戶角色的信息,那麼它可以基於這些信息做出自己的授權決策。

在這種模式中,“網關” 位於 API 和其最終用戶之間。網關可以訪問用戶信息和角色信息,它可以在將請求傳遞給 API 本身之前將這些信息附加到請求中。當 API 接收到請求時,它可以使用來自請求的角色數據(例如在請求頭中)來檢查用戶行爲是否被允許。

網關通常同時負責身份驗證和授權。例如,網關可能使用 Authorization 頭對特定用戶進行身份驗證,然後另外獲取該用戶的角色信息。然後網關將帶有用戶 ID 和角色信息的請求代理給下游服務(上面示例中的文檔服務)。

網關模式的主要好處是其架構簡單。它使下游服務(如文檔服務)的開發人員不必關心角色數據來自哪裏。授權數據在請求中始終是可用的,因此可以立即執行權限檢查,而不需要任何額外的調用。

請注意,在這裏使用明文頭信息開闢了新的攻擊途徑——你需要確保惡意客戶端不能注入它們自己的頭信息。作爲一種替代方法,用戶角色或其他訪問控制數據可以包含在他們的身份驗證令牌中,通常表示爲 JWT。

如果授權數據由少量角色組成(例如,每個用戶在一個組織中只能有一個角色),網關模式的效果最好。當權限開始不僅僅依賴於用戶在組織中的角色時,請求的規模就會激增。也許用戶可以有不同的角色,這取決於他們試圖訪問的資源類型(特定事件的組織者,或特定文件夾的編輯器)。有時,這些數據太大以至於無法放入請求頭中,而其他時候,一次獲取所有數據效率很低。如果是這種情況,將所有相關的授權數據塞到令牌或請求頭中並不能完全解決問題。

4 模式 3:集中存放所有授權數據

另一種解決方案是將所有授權數據和邏輯放在一個地方,與需要實施授權的所有服務分開。實現此模式的最常見方法是構建專用的 “授權服務”。然後,當其他服務需要執行權限檢查時,它們會轉向詢問授權服務:

在這個模型中,文檔服務根本不關心用戶的角色:它只需要詢問授權服務,用戶是否可以編輯文檔,或者用戶是否可以查看文檔。授權服務本身包含做出該決策所需的所有內容(包括角色數據)。

這可能非常有吸引力:你現在有一個負責授權的系統,這符合微服務的哲學。以這種方式進行責任分離有一些好處:團隊中的其他開發人員不需要關心授權是如何工作的。因爲它是獨立的,所以你對授權服務所做的任何優化都有助於加速整個系統的其餘部分。

當然,這種責任分離是有代價的。現在,所有授權數據都必須保存在一個地方。決策中可能使用的所有內容都必須保存在一個集中式服務中,這些內容包括用戶在組織中的身份、文檔與其組織的關係。要麼授權服務成爲該數據的唯一真實來源,要麼必須將數據從應用程序複製並同步到該中心(可能性更大)。授權系統必須理解作爲所有權限基礎的整個數據模型:組、共享、文件夾、來賓、項目。如果這些模型經常改變,授權系統可能成爲新的開發任務的瓶頸。任意微服務中的任何更改都可能需要對授權服務進行更新,從而打破你在最初轉向微服務時可能尋求的關注點分離效果。

還有其他因素會使授權服務變得棘手:部署這一負責保護每個請求的服務意味着你要負責實現高可用和低延遲。如果系統出現故障,則所有請求都會被拒絕。如果授權系統系統的響應很慢,那麼每個請求都很慢。

谷歌的 Zanzibar 論文概述了這種模式的一種實現,但它也帶來了挑戰。你必須將所有數據以 “元組” 的形式插入到 Zanzibar 中(Alice 擁有這個文檔,這個文件夾包含另一個文件夾,等等)。由於它限制了可以存儲的數據,有些規則實際上不能僅用 Zanzibar 來表示,例如一些必須與時間、請求上下文有關的規則,或者依賴於某種計算的規則。有人將這些稱之爲 “基於屬性” 的規則。例如,用戶每週只允許創建 10 個文檔,或者管理員可以將某些文件夾設置爲“只讀”,以防止對其中文檔的編輯。在這些情況下,開發人員必須在 Zanzibar 之外編寫自己的策略邏輯。

集中式授權存在的挑戰往往會阻止大多數團隊採用這種模式。採用該模式的應用往往有很多服務和足夠複雜的數據模型,接受授權服務本身增加的複雜性對它們來說是有意義的。例如,爲了從單體應用向微服務轉型,Airbnb 建立了一個名爲 Himeji 的授權服務以支持他們的授權模式。已經有一個專門的工程師團隊爲它工作了兩年,而且可能會無限期地工作下去。

但是,如果你能夠去除一些這類開銷,那麼對於許多使用微服務架構的團隊來說,集中式授權服務可能是一個很有吸引力的選擇。我的團隊正在努力構建一個授權服務,力求避免集中所有授權數據的挑戰。如果這是你所面臨的問題,請聯繫我們。

5 你應該用哪一個?

當與工程師團隊交談時,我的指導意見總是 “圍繞應用程序構建授權,而不是反過來。” 對於簡單系統,維護大量額外基礎設施代價高昂,最好將數據保存在其所在的位置,並將服務與專用 api 放在一起。某些應用程序可以通過基本用戶角色(可使用 GWT)擴展到大規模,在這種情況下,授權網關可能是最佳的。一些擁有各種產品、授權模式和用戶類型的公司可能更願意將其數據集中到專用的授權服務中。

如果你對討論你的授權系統感興趣——例如你正在考慮進行一些重構,或者只是對它的工作方式不太滿意,請與我們聯繫。你可以與 Oso 工程師安排 1x1 討論,或者加入我們的 slack。我們喜歡討論授權。

如果你曾經對混亂的授權系統感到沮喪,或者和我一樣喜歡好的分佈式系統問題,歡迎加入我們團隊!我們正在招聘,來幫助我們推動進一步的思考。

原文鏈接:

https://www.osohq.com/post/microservices-authorization-patterns

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