分佈式系統下的認證與授權

在軟件系統設計中,如何讓應用能夠在各種環境中安全高效的訪問是個複雜的問題,這個問題的背後是一系列軟件設計時需要考慮的架構安全問題:

在漫長的架構演進歷史中,業界對這些問題已經有很成熟的解決方案。在架構安全這塊,最好的是遵循技術標準與最佳實踐,儘可能不重複造輪子或 “創新”。下面這個思維導圖就是針對這些問題的常見的技術標準及方案:

在研究分佈式系統的認證和授權問題前,讓我們回到單體架構的時代,看看在單體架構上這些問題是如何被解決的。

單體系統

認證

認證主要解決 你是誰 的問題,從方式上來看有以下三種:

在單體系統時代,認證方式一般是在通信信道上開啓 HTTPS,在通信協議上利用 HTTP Basic/Digest/Bearer/HOBA/OCRA 等方式並在通信內容上結合表單或 TOTP 等的認證組合方式。這樣可以從通信的不同階段獲得相應的安全保證。

如果想對基於 HTTP 協議的認證方式做進一步的瞭解,可以參考這兩篇文章:

  1. 認證 | 鳳凰架構 

    (https://icyfenix.cn/architect-perspective/general-architecture/system-security/authentication.html)

  2. 細說 API - 認證、授權和憑證 - Thoughtworks 洞見 

    (https://insights.thoughtworks.cn/api-2/)

單點登錄(SSO

認證的一個常見應用場景是單點登錄。單點登錄主要解決了一個一次登錄訪問多個獨立應用的問題。在單點登錄方案出現之前,每個應用都需要獨立登錄維持各自的會話。相關的技術方案已經很成熟,主要有以下:

授權

授權主要解決 你能做什麼 的問題,從方案上來說有以下幾種:

如果想對授權做進一步的瞭解,可以參考這篇文章:

  1. 授權 | 鳳凰架構 

    (https://icyfenix.cn/architect-perspective/general-architecture/system-security/authorization.html)

憑證

憑證是爲了解決在認證授權後如何承載認證授權信息的問題。在單體應用時代,主流的解決方案是基於 HTTP 協議的 Cookie-Session 機制爲代表的服務端狀態存儲技術。

由於 HTTP 協議本身是無狀態的,要維持一個會話(Session),而不是每次訪問都重新認證授權,需要客戶端也就是瀏覽器通過 Cookie 來存儲服務器端返回的一個憑證信息,這個憑證信息一般是一串隨機的字符串,用來代表用戶此次的會話標識。每次請求瀏覽器都會在 HTTP Header 中攜帶這個 Cookie 信息,應用拿到這個會話標識後從內存或緩存(Cache)中查詢出用戶的信息,這樣就定位到了具體的用戶,實現了會話的維持。

這套古老的方案存在以下先天優勢:憑證 | 鳳凰架構

一切都很美好,直到我們來到了分佈式系統時代。

分佈式系統

分佈式系統與單體系統的一大區別就是狀態管理。分佈式系統通過把單體系統中有狀態的部分轉移到中間件中去管理,從而很容易做到水平擴容,提高系統峯值處理能力。在架構認證和授權部分,分佈式和單體並沒有什麼不同,唯獨有變化的在持有狀態的憑證部分。

我們知道單體應用在服務端管理用戶會話信息,客戶端只持有會話標識。如果服務端要將此用戶會話狀態轉移出去有兩種處理思路:

JWT

如果你對 JWT 不瞭解,可以先看這兩篇:

  1. JWT | 鳳凰架構 

    (https://icyfenix.cn/architect-perspective/general-architecture/system-security/credentials.html#jwt)

  2. The Hard Parts of JWT Security Nobody Talks About 

    (https://www.pingidentity.com/en/resources/blog/post/jwt-security-nobody-talks-about.html)

由於 JWT 的 Payload 並未做過多限制,所以很容易產生濫用的問題,並且帶來很多誤解。比如下面的一些問題:

相信看了上述的一些問題,你對 JWT 的 “簡單、安全” 有了新的理解。這還沒完,JWT 還有以下一些 Cookie-Session 沒有的問題:

JWT 解決了 Cookie-Session 方案在分佈式系統中因 CAP 的限制而帶來的問題,但同時也帶來了一些新的問題。所以並不能說 JWT 就是 Cookie-Session 在分佈式系統中的完美替代。

那麼 JWT 的最佳使用場景到底是什麼?這篇 Stop using JWT for sessions 給出了以下的結論:JWT 更適合作分佈式系統中的一次性令牌使用。分佈式系統繼續使用 Cookie-Session 做會話管理,但可以在認證鑑權後生成 JWT 做分佈式系統內部服務調用間的一次性令牌。

讓我們通過一個例子來理解下在分佈式系統下的認證授權場景。

一個例子

  1. 用戶通過 HTTPS 訪問我們的應用。當請求發送至微服務網關層(Gateway),網關檢測 HTTP Header 中的 Cookie 發現沒有 SESSIONID 這個鍵值對,重定向至 SSO 登錄頁面。

  2. 用戶通過 SSO 登錄我們的應用。

  3. 用戶信息存放至 AD/LDAP 等系統中。管理員提前給用戶配置好角色權限。

  4. SSO 集成方案我們選擇 OIDC。OIDC 集成了 AD/LDAP,當用戶提供正確的用戶名和密碼後,SSO 重定向至網關。

  5. 網關生成了 SESSIONID 鍵值對並通過 HTTP Set-Cookie 響應給用戶瀏覽器設置了此 Cookie。

  6. 瀏覽器重新發起帶 SESSIONID Cookie 的請求。網關經過查詢其緩存或中間件(如將會話信息存放至 Redis)中的 Session 信息確認了用戶的身份信息。之後網關請求 Auth 服務利用其私鑰簽名生成 JWT 憑證,JWT Payload 中可以存放一部分用戶信息和角色信息,這些信息可以從中間件中或 AD/LDAP 中查詢出。

  7. 網關之後將此 JWT 憑證通過反向代理轉發至內部的 BFF 服務,之後請求到達內部的領域微服務。

  8. 各領域微服務接受到請求後,先從 HTTP Header 中拿出 JWT 憑證。

  9. Auth 服務通過一個類似 https://<your_domain>/.well-known/jwks.json 的 API 提供 JWT 公鑰的分發。關於 .well-known 前綴,可閱讀 RFC 5785 做進一步瞭解。在 jwks.json 文件中,我們可以找到 JWK 或 JSON Web Key,這是我們用來驗證簽名的公鑰。

  10. 校驗 JWT 這塊邏輯屬於微服務共有的部分,一般可以開發一個 SDK 包來做這個通用的工作。爲了提高性能,可使用緩存技術,定時從 Auth 中同步公鑰。

  11. 在執行真正的業務邏輯前,先利用之前定時從 Auth 服務中同步獲取的公鑰。

  12. 獲取到公鑰後驗證成功後拿出 JWT Payload 即可獲取到用戶信息和角色權限。

全部流程就是這樣,我們得到了以下的一些好處:

架構總是在演進,也許分佈式系統中很多問題我們還沒完全解決,就來到了雲原生時代。

雲原生系統

如果你對雲原生應用開發還不瞭解的話,可以先看看我這篇 K8S 雲原生應用開發小記。雲原生系統其實並不是什麼後分布式系統時代。它們兩者都是爲了解決不同場景的問題而出現的解決方案。

在認證授權這塊,雲原生系統的優勢在於可以通過 服務網格 (Service Mesh) 做一些業務系統中通用的切面工作,比如我們在分佈式系統中遇到的校驗 JWT 的 SDK 其實就可以放入服務網格中的邊車(Sidecar)去實現,讓業務應用更專注特定領域的業務。

由於這篇文章並不主要討論雲原生,對這部分感興趣的可以參考以下兩篇文章做進一步瞭解:

  1. Service Mesh 架構下的認證與授權

    (https://insights.thoughtworks.cn/service-mesh-authentication-authorization/)

  2. 微服務下的身份認證和令牌管理

    (https://insights.thoughtworks.cn/microservices-authentication-token-management/)

總結

由於篇幅及能力限制,這篇文章我只能從高層次梳理在不同架構演進中認證、授權及憑證這些和架構安全相關的技術的發展過程。由於這些技術涉及了大量的技術標準及實踐,很難在一篇文章中對這些技術做詳盡的分享,更無法去分享如何實現。但有了這些理論支持和最佳實踐,希望能讓你在實現的過程中多了一個指引。如果你想進一步瞭解,可參考文章中的參考文章鏈接。

最後,技術總是在不斷的發展,但並不是新技術總比老技術 “先進”。正如文章中對 Cookie-Session 與 JWT 的分析對比,技術方案總是充滿了各種 Trade-off。而作爲一個工程師,我們能做的就是認清這些技術的歷史背景及侷限性,選擇最適合項目需求的技術方案。


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