在Keycloak中實現多租戶併在ASP.NET Core下進行驗證

来源:https://www.cnblogs.com/daxnet/p/18115003
-Advertisement-
Play Games

Keycloak是一個功能強大的開源身份和訪問管理系統,提供了一整套解決方案,包括用戶認證、單點登錄(SSO)、身份聯合、用戶註冊、用戶管理、角色映射、多因素認證和訪問控制等。它廣泛應用於企業和雲服務,可以簡化和統一不同應用程式和服務的安全管理,支持自托管或雲部署,適用於需要安全、靈活且易於擴展的用 ...


Keycloak是一個功能強大的開源身份和訪問管理系統,提供了一整套解決方案,包括用戶認證、單點登錄(SSO)、身份聯合、用戶註冊、用戶管理、角色映射、多因素認證和訪問控制等。它廣泛應用於企業和雲服務,可以簡化和統一不同應用程式和服務的安全管理,支持自托管或雲部署,適用於需要安全、靈活且易於擴展的用戶身份管理和訪問控制的場景。

SaaS(Software as a Service,軟體即服務)是一種軟體分發模式,其中軟體應用程式通過互聯網托管並由服務提供商管理。用戶通常通過訂閱模式訪問這些服務,而不是購買和安裝軟體。SaaS應用程式通常部署在雲環境中,只要用戶有互聯網連接,他們就可以隨時隨地訪問服務。在SaaS應用中,多租戶支持是關鍵需求之一。在多租戶環境中,一個服務實例需要為多個租戶(即不同的公司、組織或個人用戶)提供服務,同時保障數據隔離和安全性。因此,多租戶的支持也成為了與SaaS應用集成的身份和訪問管理(IAM)服務的基本需求,在選用Keycloak作為SaaS應用IAM服務的場景下,對於多租戶模式的支持,也是Keycloak實施的一個關鍵需求。

Keycloak中的基本概念及其之間的關係

要理解Keycloak對於多租戶的支持方式,首先需要瞭解Keycloak中的一些基本概念以及它們之間的關係。當然,本文不會詳細解釋這些概念是什麼,Keycloak的官方文檔里有詳細的解釋,瞭解這部分內容將有助於選擇以何種方式實現多租戶。

下圖簡要描述了Keycloak中的一些主要的基本概念及其之間的關係:

在一個Keycloak的部署中包含多個Realm,Realm是一個管理的域,它作為頂層組織單元,用來封裝和管理一組用戶、角色、群組和客戶端(即應用程式或服務)。每個Realm可以有自己的一套用戶目錄、身份驗證和授權規則、客戶端配置以及其他安全性設置,不同的Realm之間是相互隔離的。在一個Realm中,可以創建多個Client,Client是一個非常重要的概念,它代表了可以請求Keycloak進行認證和授權的實體。在OAuth 2.0和OpenID Connect(OIDC)的上下文中,客戶端通常指的是一個應用程式,它希望使用Keycloak作為身份提供者來驗證用戶的身份,並可能獲取用戶授權的令牌,以便訪問受保護的資源。在不同的Client下,可以創建互相隔離的一組Client Role,也就是Client下的角色,一個角色可以關聯到多個用戶(user)或者用戶組(group)上。與此同時,Client下還可以有一組Client Scope,而Client Scope還可以映射到某個或者某些用戶信息屬性(user profile attribute)上,而用戶信息屬性(user profile attribute)則可以是定義在用戶(user)上的自定義屬性。

Keycloak中多租戶的不同模式

從上面的結構可以分析得出,Keycloak對多租戶的支持主要有兩種模式:

  1. 每個租戶(tenant)使用獨立的Realm,即Realm per tenant模式
  2. 所有租戶共用同一個Realm,但使用不同的Client,即Single Realm模式

需要根據不同需求來選擇不同的模式。

Realm per Tenant

這種模式的實現比較簡單清晰:數據嚴格隔離,簡單地按照上圖中的關係對Realm進行配置就行了,比如,在Realm下可以直接創建用戶組,這些用戶組就是屬於當前這個租戶的;但另一方面,由於不同的Realm擁有完全不同的一套用戶(user)和用戶組(group)數據,於是,Realm per Tenant無法實現某個用戶同時屬於多個租戶的場景。你可以在不同的租戶中創建名字和電子郵件地址完全相同的用戶,但是,它們雖然名字相同,卻是完全不同的兩個實體。除此之外,當租戶數量越來越大時,Realm的個數也會相應增加,對於Keycloak來說也存在一定的性能影響(我沒有實際測試過,但是根據目前來自各方面的文檔資料,Keycloak維護上百個Realm還是沒有太大問題的)。

Single Realm

在Single Realm模式下,每個租戶就是一個Client,好處是用戶及用戶組可以屬於多個租戶,比如:A用戶可以同時屬於T1和T2兩個租戶,也不必考慮Realm太多影響性能。每個Client都可以配置不同的登錄選項,因此,可以滿足多租戶的需求。但實現這種模式相對比較複雜,而且有些信息比如用戶的角色、所屬的用戶組等會全部出現在用戶的access token里,而這些信息有些是屬於特定租戶的,其數據隔離性沒有Realm per Tenant模式好。

使用Single Realm實現多租戶的一般思路是,針對每一個租戶創建一個Client,所以在這個Realm下,用戶是跨租戶的,用戶組理論上也是跨租戶的,但是,可以對不同的租戶,設置不同的用戶組,然後在這個租戶級別的用戶組下,還可以創建子組,所以,用戶組也可以做到按租戶隔離。對於角色而言,Client下本身就可以分配不同的角色,所要做的就是將用戶/用戶組分配到不同的角色上,只不過選擇角色的時候需要指定,是分配到哪個Client下的哪個角色。

演練:在Keycloak中配置Single Realm多租戶模式

註意:本文演練部分基於Keycloak 24.0.2,對於其它版本的Keycloak,界面操作可能會有出入。

創建一個新的Realm

啟動Keycloak本地開發環境,推薦使用docker,可以參考這篇文章。啟動成功後,用admin/admin登錄管理界面,點擊界面左邊的Keycloak下拉框,然後點擊Create realm按鈕,以創建一個新的Realm。

在Create realm界面,只需要輸入Realm name即可,例如:multitenant,然後點擊Create按鈕,系統提示Realm created successfully,表示Realm創建成功。

創建對應於租戶的Client

接下來,在側邊欄中點擊Clients鏈接,打開Clients列表界面,在界面中,點擊Create client,註意:這裡我們就是為某個租戶創建一個Client,因此,可以考慮用租戶的名稱來命名這個Client,假設我們客戶的名字叫Globex Corporation,於是,Client就命名為globex。在Create client界面中,Client ID填globex,然後點擊Next按鈕:

在Capability config下,啟用Client authentication,Authorization也可以考慮啟用,在Authentication flow中,暫時先勾選Direct access grants,以便後面的測試。在OIDC的Authorization Code Flow和Authorization Code Flow /w PKCE兩種flow下,該選項可以關閉,當然,具體的可以根據項目實際情況選擇。這部分的配置如下:

點擊Next按鈕,在Login settings頁面,可以無需修改配置,直接點擊Save按鈕,此時Client創建成功。

配置Client scopes

在左側側邊欄,點擊Client scopes,然後點擊Create client scope按鈕,在Create client scope頁面中,輸入Client scope名稱,其它的預設就行,然後點擊Save按鈕:

在新創建的Client scope下,點擊Mappers標簽頁,然後點擊Configure a new mapper按鈕:

在Configure a new mapper列表中,找到Group Membership,直接點擊選中,然後在Add mapper頁面下,輸入Mapper的名字,然後Token Claim Name也指定一下,其它的開關按鈕保持預設就行。這裡註意的是,Token Claim Name並不是必須的,但是如果你希望它出現在用戶的access token中,那麼這個是必填的:

完成配置後點擊Save按鈕保存配置。然後回到Clients列表,點擊剛剛新建的Client:globex,進入Client scopes標簽頁,單擊Add client scope按鈕:

在彈出的Add client scopes to globex對話框中,選擇剛剛新建的Client scope,然後點擊Add -> Default按鈕:

配置用戶(user)和用戶組(group)

註意:此處配置的用戶和用戶組是跨多租戶,也就是跨多個Client的,之後會在Client的Role下通過角色關聯來將用戶關聯到租戶上。

在左邊側邊欄點擊Users鏈接,在Users界面中,點擊Create new user按鈕,然後在General部分輸入Username,其它信息可以按需填寫,然後點擊Create按鈕:

在用戶詳細信息頁面中,點擊Credentials選項卡,然後點擊Set password按鈕:

在彈出的對話框中,輸入密碼,然後關閉Temporary選項,再點擊Save按鈕,在確認對話框中,點擊紅色的Save password按鈕即可:

在左側側邊欄點擊Groups鏈接,然後點擊Create group按鈕,在彈出的Create a group對話框中,輸入組名。由於我們希望不同租戶具有不同的組配置策略,也就是租戶的用戶組也需要隔離,因此,在這裡組名就使用租戶的名稱,也就是globex。然後點擊Create按鈕創建組。在成功創建組之後,點擊Members選項卡,然後點擊Add member按鈕,在彈出的Add member對話框中,將剛剛新建的super用戶添加到globex組中。

一個對應於租戶的組(比如這裡的globex組)下還可以創建多個子用戶組,也就可以定義針對不同租戶的用戶組層次結構。

配置用戶/用戶組角色

點擊左邊側邊欄的Clients鏈接,在Clients列表中,點擊globex Client,在Roles選項卡下,點擊Create role按鈕,新建一個角色,比如administrator,然後點擊Save按鈕:

回到Users或者Groups界面,選擇某個用戶或者用戶組,比如選擇剛剛新建的super用戶,在用戶的Role mapping選項卡下,點擊Assign role按鈕:

在Assign roles to super對話框中,點擊Filter by realm roles下拉框,選擇Filter by clients,然後選擇globex這個Client的administrator角色,表示需要將globex租戶的administrator角色分配給super用戶:

測試我們的配置

點擊左側側邊欄的Clients鏈接,然後選擇globex Client,進入Credentials選項卡,在Client Secret部分點擊複製按鈕,將Client Secret複製到剪貼板中,然後打開Postman,使用下麵的HTTP請求:

  • POST URL為http://<server>:<port>/realms/<realm>/protocol/openid-connect/token
  • grant_type:password
  • client_id:租戶的名稱,globex
  • client_secret:上一步複製到剪貼板的內容
  • password:用戶密碼
  • username:用戶名稱

點擊Send按鈕後,即可得到access token。打開https://jwt.io,將access token複製到Debugger的Encoded部分,即可解出access token的明文:

詳細內容類似如下:

{
  "exp": 1712410272,
  "iat": 1712409972,
  "jti": "8f18192e-c223-4f60-a23d-6e825f5e6b37",
  "iss": "http://localhost:5600/realms/multitenant",
  "aud": "account",
  "sub": "1a666edc-36c2-4704-ac14-2a8d88332781",
  "typ": "Bearer",
  "azp": "globex",
  "session_state": "448c536c-3f11-4275-b03c-78bec00deb56",
  "acr": "1",
  "allowed-origins": [
    "/*"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization",
      "default-roles-multitenant"
    ]
  },
  "resource_access": {
    "globex": {
      "roles": [
        "administrator"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "email profile",
  "sid": "448c536c-3f11-4275-b03c-78bec00deb56",
  "email_verified": false,
  "name": "Super Admin",
  "groups": [
    "/globex"
  ],
  "preferred_username": "super",
  "given_name": "Super",
  "family_name": "Admin",
  "email": "[email protected]"
}

可以看到:

  1. azp代表了當前請求access token的租戶名稱
  2. resource_access下列出了該用戶所屬的租戶名稱,以及在每個租戶下所屬的角色
  3. groups下列出了該用戶所屬的用戶組,很遺憾,這裡它會將所有該用戶所屬的用戶組列出來,比如,如果還存在另一個租戶soylent,那麼,這個groups數組裡就會出現/soylent這個項目。在實際項目中,可以在Action Filter中,通過代碼來處理這個信息

與ASP.NET Core Web API的集成

此時可以新建一個ASP.NET Core Web API應用程式,集成Keycloak身份認證,來查看在User Principle上可以拿到哪些數據。修改Program.cs文件,加入這些代碼:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "http://localhost:5600/realms/multitenant";
        options.MetadataAddress = 
            "http://localhost:5600/realms/multitenant/.well-known/openid-configuration";
        options.RequireHttpsMetadata = false;
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            NameClaimType = "preferred_username",
            RoleClaimType = ClaimTypes.Role,
            ValidateIssuer = true,
            ValidateAudience = false
        };
    });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

然後,在Controller上加入[Authorize]特性:

[ApiController]
[Authorize]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
  // 省略N行
}

在某個Controller Action方法上設置一個調試斷點,然後啟動ASP.NET Core Web API應用程式。接下來,在Postman中調用這個設置了調試斷點的API服務,記得Authorization選擇Bearer Token,然後將上面通過Postman獲得的access_token作為Bearer Token填入,發起請求。此時,Visual Studio中斷點會被命中,查看User對象,可以看到,有關租戶的一些信息已經在User的Claims下:

之後,只需利用這些信息來實現用戶授權即可。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文探討了.NET Emit 入門教程的第六部分,聚焦於ILGenerator中的方法調用指令。通過詳細分析 ILGenerator 的使用方法和方法調用指令,讀者可以更深入地瞭解.NET平臺下動態生成代碼的實現機制。通過本文的閱讀,讀者可以更加熟練地使用 ILGenerator 來動態生成高效、靈... ...
  • 自上一個系列文章寫完之後,最近的兩三個月時間,一直在寫WPF相關技術文章,斷斷續續地寫了近二十幾篇文章,為了方便大家閱讀,現將本系列文章分類整理,彙總如下。 ...
  • 今天給大家上個硬貨,下拉多選框,同時也是下拉多選樹,支持父節點跟子節點!該控制項是基於Telerik控制項封裝實現的,所以大家在使用的過程中需要引用Telerik.WinControls.dll、Telerik.WinControls.UI.dll,還有一些相關的類庫,大家有需要的可以去網上自己找,另外 ...
  • 在.NET Emit 入門教程的第六部分中,我們深入探討了 ILGenerator 指令方法,特別是關於創建實例指令的詳細解釋。ILGenerator 是.NET框架中的一個強大工具,用於在運行時生成和執行IL代碼。在這篇文章中,我們學習瞭如何使用 ILGenerator 來創建實例,其中主要涉及到... ...
  • 一:背景 1. 講故事 前些天有位朋友微信找到我,說他們的WPF程式有記憶體泄漏的情況,讓我幫忙看下怎麼回事?並且dump也抓到了,網上關於程式記憶體泄漏,記憶體暴漲的文章不計其數,看樣子這個dump不是很好分析,不管怎麼說,上 windbg 說話。 二:WinDbg分析 1. 記憶體真的暴漲嗎 在.NET ...
  • Avalonia中的自定義用戶控制項 Avalonia是一個跨平臺的.NET UI框架,它允許開發者使用C#和XAML來構建豐富的桌面應用程式。 自定義用戶控制項(UserControl)是Avalonia中一種重要的組件,它允許我們將多個控制項組合成一個可重用的單元。 本文將介紹如何在Avalonia中 ...
  • 一.控制項介紹 初看標題可能無法理解,我們看看什麼是自適應排列。 乍一看它有點像WrapPanel控制項,都是從左至右排列,如果一行排列不下就換行繼續排列,但是細看你就會發現不對,WrapPanel控制項行尾是不會對齊的,也就是說只要WrapPanel的子控制項的寬度不一致,每一行的末尾就會必定留下一段空白 ...
  • 引言 在我們上一篇文章瞭解了單元測試的基本概念和用法之後,今天我們來聊一下 TDD(測試驅動開發) 測試驅動開發 (TDD) 測試驅動開發英文全稱是Test Driven Development 簡稱 TDD。 根據 UncleBob 的 TDD 描述總結 我們先創建一個測試項目 直接在 VS 創建 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...