NextUI.Identity.Admin 真实管理功能实施方案
一、问题分析
当前状态
| 层次 | 状态 | 说明 |
|---|---|---|
| Keycloak Admin API Client | ✅ 完整 | KeycloakAdminClient 已实现所有 REST API 调用 |
| Admin Provider 抽象 | ✅ 完整 | IAdminProvider, KeycloakAdminProvider 已实现 |
| 服务器注册 | ✅ 部分 | ServerRegistry, AdminContextService 已实现 |
| UI 组件 | ⚠️ 只有皮 | 组件存在但需要外部传入 handlers,默认显示 mock |
| 页面集成 | ❌ 缺失 | 没有开箱即用的管理页面 |
核心问题
目前的 SxAdminUserList 组件:
┌─────────────────────────────────────────────────────┐
│ SxAdminUserList │
│ ├── LoadUsersHandler = null → 显示 mock 数据 │
│ ├── OnUserCreated = null → 什么都不做 │
│ └── OnUserDeleted = null → 什么都不做 │
└─────────────────────────────────────────────────────┘
期望的效果:
┌─────────────────────────────────────────────────────┐
│ SxAdminUserList (自动连接真实服务) │
│ ├── 自动从 AdminContext 获取当前服务器 │
│ ├── 自动调用 KeycloakAdminClient 获取用户列表 │
│ ├── 创建用户 → 真正调用 Keycloak API │
│ └── 删除用户 → 真正调用 Keycloak API │
└─────────────────────────────────────────────────────┘
二、解决方案
方案概述
创建 AdminPage 系列组件,它们:
- 自动注入
AdminContextService获取当前管理上下文 - 自动使用
AdminProviderFactory获取对应的 provider - 内部调用现有的 UI 组件并自动传入真实的 handlers
架构设计
┌─────────────────────────────────────────────────────────────────────┐
│ 应用使用层 │
│ │
│ 使用方式 1: 直接使用预组装页面 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ <SxAdminUserManagementPage /> │ │
│ │ - 自动获取当前服务器上下文 │ │
│ │ - 自动调用真实 API │ │
│ │ - 包含完整 CRUD 功能 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 使用方式 2: 使用智能组件 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ <SxAdminUserListConnected /> │ │
│ │ - 自动连接到真实服务 │ │
│ │ - 可以自定义部分行为 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 使用方式 3: 使用原始 UI 组件 + 手动 wiring (现有方式) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ <SxAdminUserList LoadUsersHandler="@MyHandler" ... /> │ │
│ │ - 完全手动控制 │ │
│ │ - 最大灵活性 │ │
│ └─────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ 服务/Provider 层 │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ AdminContextService │ │ AdminProviderFactory │ │
│ │ - 当前选中的服务器 │ │ - 根据类型创建 provider│ │
│ │ - 当前 Realm/Client │ │ - Keycloak/Auth0/...│ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ KeycloakAdminProvider│ │SharedProfileAdminProv│ │
│ │ - 用户/角色/组管理 │ │ - SharedProfile CRUD │ │
│ └──────────────────────┘ └──────────────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ API Client 层 │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ KeycloakAdminClient │ │ SharedProfileClient │ │
│ │ - 真实 HTTP 调用 │ │ - 真实 HTTP 调用 │ │
│ │ - 完整 Admin REST API│ │ - 标准 Admin API │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
三、实施步骤
Phase 1: 完善 Admin Provider 层 (服务层)
目标: 确保 Provider 层能正确调用真实 API
1.1 完善 KeycloakAdminProvider
文件: src/NextUI.Identity/Admin/Providers/KeycloakAdminProvider.cs
public class KeycloakAdminProvider : IAdminProvider
{
private readonly KeycloakAdminClient _client;
private readonly string _realm;
// 实现所有管理操作,调用 _client 的真实 API
public async Task<PagedResult<AdminUser>> GetUsersAsync(UserQuery query)
{
var users = await _client.GetUsersAsync(_realm, query);
var count = await _client.GetUsersCountAsync(_realm, query.Search);
return new PagedResult<AdminUser>
{
Items = users.Select(MapToAdminUser).ToList(),
TotalCount = count
};
}
public async Task CreateUserAsync(AdminUser user)
{
var kcUser = MapToKeycloakUser(user);
await _client.CreateUserAsync(_realm, kcUser);
}
// ... 其他操作
}
1.2 完善 SharedProfileAdminProvider
文件: src/NextUI.Identity/Admin/Providers/SharedProfileAdminProvider.cs
public class SharedProfileAdminProvider : ISharedProfileAdminService
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public async Task<PagedResult<SharedProfile>> GetProfilesAsync(int skip, int take)
{
var response = await _httpClient.GetAsync($"{_baseUrl}?skip={skip}&take={take}");
return await response.Content.ReadFromJsonAsync<PagedResult<SharedProfile>>();
}
public async Task UpdateProfileAsync(SharedProfile profile)
{
await _httpClient.PutAsJsonAsync($"{_baseUrl}/{profile.UserId}", profile);
}
}
1.3 修复 AdminProviderFactory
确保能正确创建和配置 providers。
Phase 2: 创建 Connected 组件
目标: 创建自动连接真实服务的"智能"组件
2.1 SxAdminUserListConnected
文件: src/NextUI.Blazor/Components/Admin/SxAdminUserListConnected.razor
@inject AdminContextService AdminContext
@inject AdminProviderFactory ProviderFactory
@if (!_initialized)
{
<SxProgressRing />
}
else if (_provider == null)
{
<SxMessageBar Intent="MessageIntent.Warning">
Please select a server to manage.
</SxMessageBar>
}
else
{
<SxAdminUserList
LoadUsersHandler="LoadUsersAsync"
OnUserCreated="CreateUserAsync"
OnUserEdited="UpdateUserAsync"
OnUserDeleted="DeleteUserAsync"
OnUserStatusChanged="ToggleUserStatusAsync"
OnPasswordReset="ResetPasswordAsync"
LoadRolesHandler="LoadRolesAsync"
OnRolesChanged="UpdateRolesAsync" />
}
@code {
private IAdminProvider? _provider;
private bool _initialized;
protected override async Task OnInitializedAsync()
{
AdminContext.ContextChanged += OnContextChanged;
await InitializeProvider();
}
private async Task InitializeProvider()
{
var server = AdminContext.CurrentServer;
if (server != null)
{
_provider = await ProviderFactory.CreateProviderAsync(server);
}
_initialized = true;
}
private async Task<(List<AdminUser>, int)> LoadUsersAsync(UserSearchRequest request)
{
var result = await _provider!.GetUsersAsync(new UserQuery
{
Search = request.Search,
First = request.First,
Max = request.Max
});
return (result.Items, result.TotalCount);
}
private async Task CreateUserAsync(AdminUser user)
{
await _provider!.CreateUserAsync(user);
}
// ... 其他方法
}
2.2 类似创建其他 Connected 组件
SxAdminRoleListConnectedSxAdminClientListConnectedSxSharedProfileListConnected
Phase 3: 创建预组装管理页面
目标: 提供开箱即用的完整管理页面
3.1 SxAdminUserManagementPage
文件: src/NextUI.Blazor/Components/Admin/Pages/SxAdminUserManagementPage.razor
@namespace NextUI.Blazor.Components.Admin.Pages
<SxStack Orientation="StackOrientation.Vertical" Gap="ControlSize.Large">
@* 页面标题 *@
<SxStack Orientation="StackOrientation.Horizontal"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.SpaceBetween">
<SxTypography Variant="TypographyVariant.Headline" Level="HeadlineLevel.H1">
User Management
</SxTypography>
<SxAdminServerSelector />
</SxStack>
@* 用户列表 (自动连接) *@
<SxAdminUserListConnected />
</SxStack>
3.2 SxAdminRoleManagementPage
<SxStack Orientation="StackOrientation.Vertical" Gap="ControlSize.Large">
<SxTypography Variant="TypographyVariant.Headline" Level="HeadlineLevel.H1">
Role Management
</SxTypography>
<SxAdminServerSelector />
<SxTabs>
<SxTabList>
<SxTab>Realm Roles</SxTab>
<SxTab>Client Roles</SxTab>
</SxTabList>
<SxTabPanel>
<SxAdminRoleListConnected RoleScope="RoleScope.Realm" />
</SxTabPanel>
<SxTabPanel>
<SxAdminClientSelector />
<SxAdminRoleListConnected RoleScope="RoleScope.Client" />
</SxTabPanel>
</SxTabs>
</SxStack>
3.3 SxAdminDashboardPage
首页仪表板,显示:
- 服务器连接状态
- 用户统计
- 快捷操作
Phase 4: 服务器管理功能
目标: 实现真正的服务器配置和管理
4.1 服务器配置持久化
文件: src/NextUI.Identity/Admin/Services/ServerConfigStorage.cs
public interface IServerConfigStorage
{
Task<List<AdminServer>> LoadServersAsync();
Task SaveServersAsync(List<AdminServer> servers);
Task AddServerAsync(AdminServer server);
Task UpdateServerAsync(AdminServer server);
Task RemoveServerAsync(string serverId);
}
// 浏览器实现 (localStorage)
public class BrowserServerConfigStorage : IServerConfigStorage
{
private readonly IJSRuntime _js;
public async Task<List<AdminServer>> LoadServersAsync()
{
var json = await _js.InvokeAsync<string?>("localStorage.getItem", "nextui_admin_servers");
return json == null ? [] : JsonSerializer.Deserialize<List<AdminServer>>(json)!;
}
public async Task SaveServersAsync(List<AdminServer> servers)
{
var json = JsonSerializer.Serialize(servers);
await _js.InvokeVoidAsync("localStorage.setItem", "nextui_admin_servers", json);
}
}
4.2 服务器连接测试
public class ServerConnectionTester
{
public async Task<ConnectionTestResult> TestConnectionAsync(AdminServer server)
{
try
{
var provider = await _factory.CreateProviderAsync(server);
// 尝试获取 realm 信息
if (server.Type == AdminServerType.Keycloak)
{
var realms = await provider.GetRealmsAsync();
return new ConnectionTestResult
{
Success = true,
Message = $"Connected. Found {realms.Count} realms."
};
}
return new ConnectionTestResult { Success = true };
}
catch (Exception ex)
{
return new ConnectionTestResult
{
Success = false,
Message = ex.Message
};
}
}
}
Phase 5: 完整管理门户模板
目标: 提供独立部署的管理门户项目模板
5.1 项目模板
templates/NextUI.Templates.AdminPortal/
├── Program.cs
├── appsettings.json
├── Pages/
│ ├── Index.razor → 仪表板
│ ├── Users.razor → 用户管理
│ ├── Roles.razor → 角色管理
│ ├── Clients.razor → 客户端管理
│ ├── SharedProfiles.razor → 共享配置管理
│ └── Servers.razor → 服务器管理
└── Shared/
└── AdminLayout.razor → 管理后台布局
5.2 CLI 创建命令
nx new my-admin-portal --template admin-portal
# 生成配置文件和初始代码
# 配置 Keycloak 连接
# 启动管理门户
四、实施顺序和优先级
| 阶段 | 优先级 | 预计工作量 | 内容 |
|---|---|---|---|
| Phase 1 | 🔴 高 | 4h | 完善 KeycloakAdminProvider 和 SharedProfileAdminProvider |
| Phase 2 | 🔴 高 | 4h | 创建 Connected 组件系列 |
| Phase 3 | 🟡 中 | 3h | 创建预组装管理页面 |
| Phase 4 | 🟡 中 | 3h | 服务器管理功能 |
| Phase 5 | 🟢 低 | 4h | 管理门户模板 |
五、验证清单
Phase 1 验证
- 启动 Keycloak (
./nx identity demo start) - 调用
KeycloakAdminProvider.GetUsersAsync()返回真实用户 - 创建用户后在 Keycloak Admin Console 可见
- 删除用户后在 Keycloak 中确实消失
Phase 2 验证
- 放置
<SxAdminUserListConnected />显示真实用户列表 - 点击 "Add User" 创建真实用户
- 点击删除按钮真正删除用户
- 重置密码功能正常工作
Phase 3 验证
- 放置
<SxAdminUserManagementPage />显示完整管理页面 - 切换服务器后数据自动刷新
- 所有 CRUD 操作正常
Phase 4 验证
- 添加新服务器并保存
- 测试连接功能工作
- 刷新页面后服务器配置仍然存在
六、技术决策
| 决策点 | 方案 | 理由 |
|---|---|---|
| 组件设计 | Connected + Raw 双层 | 灵活性 + 开箱即用 |
| 状态管理 | AdminContextService 单例 | 跨组件共享当前服务器上下文 |
| 服务器配置存储 | localStorage | 简单、不需要后端 |
| 认证 | 使用当前用户 Token | 统一认证流程 |
| 错误处理 | Toast 通知 | 用户友好 |
七、当前已有的基础设施
已实现 ✅
KeycloakAdminClient- 完整的 Keycloak Admin REST API 客户端IAdminProvider接口定义KeycloakAdminProvider部分实现SharedProfileAdminProvider部分实现ServerRegistry服务器注册AdminContextService管理上下文- UI 组件 (SxAdminUserList, SxAdminRoleList 等)
需要完善 ⚠️
- Provider 的完整 CRUD 实现
- Connected 组件
- 预组装页面
需要新增 ❌
SxAdminUserListConnected等 Connected 组件SxAdminUserManagementPage等预组装页面- 服务器配置持久化
- 连接测试功能