基于C# Microsoft Identity的Blazor Server内置授权UI实现代码咨询
Blazor Server 接入Azure AD后应用内授权管理UI实现方案
你已经完成Azure AD身份校验的前提下,仅需要扩展授权逻辑即可在自有应用内实现全部角色、用户的管理操作,无需跳转至Azure门户。以下是可直接参考的实现代码:
第一步:扩展身份声明,同步角色到登录上下文
首先自定义声明转换器,将用户的角色信息(无论存储在本地数据库还是Azure AD中)附加到登录用户的Claims中,方便后续授权校验:
public class CustomClaimsTransformer : IClaimsTransformation { private readonly IUserRoleService _userRoleService; public CustomClaimsTransformer(IUserRoleService userRoleService) { _userRoleService = userRoleService; } public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { if (principal.Identity?.IsAuthenticated == true) { // 从Azure AD返回的声明中获取用户唯一标识 var userOid = principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier"); if (!string.IsNullOrEmpty(userOid)) { // 读取用户所属角色,支持从本地DB或者Azure Graph API读取 var userRoles = await _userRoleService.GetUserRolesAsync(userOid); foreach (var role in userRoles) { ((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.Role, role)); } } } return principal; } }
在Program.cs中注册服务:
builder.Services.AddScoped<IClaimsTransformation, CustomClaimsTransformer>(); // 注册自定义的用户角色服务,可根据存储位置替换实现类 builder.Services.AddScoped<IUserRoleService, UserRoleService>();
第二步:角色管理页面实现
创建Pages/Admin/RoleManagement.razor组件,实现角色的增删改查操作:
@page "/admin/roles" @attribute [Authorize(Roles = "Admin")] @inject IUserRoleService UserRoleService <h3>角色管理</h3> <!-- 新增角色区域 --> <div class="mb-3"> <input @bind="newRoleName" placeholder="输入角色名称" class="form-control w-25 d-inline me-2" /> <button class="btn btn-primary" @onclick="AddNewRole">新增角色</button> </div> <!-- 角色列表 --> <table class="table table-striped"> <thead> <tr> <th>角色名称</th> <th>关联用户数</th> <th>操作</th> </tr> </thead> <tbody> @foreach (var role in roleList) { <tr> <td>@role.RoleName</td> <td>@role.UserCount</td> <td> <button class="btn btn-sm btn-outline-secondary me-2" @onclick="() => EditRole(role)">编辑</button> <button class="btn btn-sm btn-outline-danger" @onclick="() => DeleteRole(role.RoleId)">删除</button> </td> </tr> } </tbody> </table> @code { private string newRoleName; private List<RoleDto> roleList = new(); protected override async Task OnInitializedAsync() { roleList = await UserRoleService.GetAllRolesAsync(); } private async Task AddNewRole() { if (!string.IsNullOrWhiteSpace(newRoleName)) { await UserRoleService.CreateRoleAsync(newRoleName); newRoleName = string.Empty; roleList = await UserRoleService.GetAllRolesAsync(); } } private async Task EditRole(RoleDto role) { // 可扩展弹窗实现角色名称、权限配置修改 await UserRoleService.UpdateRoleAsync(role); roleList = await UserRoleService.GetAllRolesAsync(); } private async Task DeleteRole(Guid roleId) { await UserRoleService.DeleteRoleAsync(roleId); roleList = await UserRoleService.GetAllRolesAsync(); } }
第三步:用户角色分配页面实现
创建Pages/Admin/UserRoleAssignment.razor组件,实现用户搜索、角色分配操作:
@page "/admin/user-roles" @attribute [Authorize(Roles = "Admin")] @inject IUserRoleService UserRoleService @inject IGraphClientService GraphClientService <h3>用户角色分配</h3> <!-- 用户搜索 --> <div class="mb-3"> <input @bind="searchKeyword" placeholder="输入用户姓名/邮箱搜索" class="form-control w-25 d-inline me-2" /> <button class="btn btn-primary" @onclick="SearchUsers">搜索</button> </div> <!-- 用户列表 --> <table class="table table-striped"> <thead> <tr> <th>用户姓名</th> <th>用户邮箱</th> <th>当前角色</th> <th>操作</th> </tr> </thead> <tbody> @foreach (var user in userList) { <tr> <td>@user.DisplayName</td> <td>@user.UserPrincipalName</td> <td>@string.Join("、", user.AssignedRoles)</td> <td> <button class="btn btn-sm btn-outline-primary" @onclick="() => OpenAssignRoleModal(user)">分配角色</button> </td> </tr> } </tbody> </table> <!-- 角色分配弹窗 --> @if (selectedUser != null) { <div class="modal show d-block" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">为 @selectedUser.DisplayName 分配角色</h5> <button type="button" class="btn-close" @onclick="CloseModal"></button> </div> <div class="modal-body"> @foreach (var role in allRoles) { <div class="form-check"> <input class="form-check-input" type="checkbox" checked="@selectedUser.AssignedRoles.Contains(role.RoleName)" @onchange="(e) => ToggleRoleSelection(role.RoleName, (bool)e.Value)" /> <label class="form-check-label">@role.RoleName</label> </div> } </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" @onclick="CloseModal">取消</button> <button type="button" class="btn btn-primary" @onclick="SaveRoleAssignment">保存</button> </div> </div> </div> </div> } @code { private string searchKeyword; private List<AdUserDto> userList = new(); private List<RoleDto> allRoles = new(); private AdUserDto selectedUser; private List<string> selectedRoles = new(); protected override async Task OnInitializedAsync() { allRoles = await UserRoleService.GetAllRolesAsync(); } private async Task SearchUsers() { // 调用Graph API搜索Azure AD用户,无需跳转Azure门户 userList = await GraphClientService.SearchUsersAsync(searchKeyword); // 同步获取用户已分配的角色 foreach (var user in userList) { user.AssignedRoles = await UserRoleService.GetUserRolesAsync(user.Oid); } } private void OpenAssignRoleModal(AdUserDto user) { selectedUser = user; selectedRoles = user.AssignedRoles.ToList(); } private void ToggleRoleSelection(string roleName, bool isSelected) { if (isSelected) { if (!selectedRoles.Contains(roleName)) selectedRoles.Add(roleName); } else { selectedRoles.Remove(roleName); } } private async Task SaveRoleAssignment() { await UserRoleService.AssignRolesToUserAsync(selectedUser.Oid, selectedRoles); selectedUser.AssignedRoles = selectedRoles.ToList(); CloseModal(); } private void CloseModal() { selectedUser = null; selectedRoles.Clear(); } }
补充说明
- 上述示例中的
IUserRoleService、IGraphClientService为抽象服务,你可以根据自己的存储需求实现对应逻辑:如果需要把角色存在Azure AD中,就把服务实现替换为调用Microsoft Graph API的逻辑即可,前端UI无需修改 - 页面访问权限可通过
[Authorize]特性或者Blazor原生的<AuthorizeView>组件控制,仅允许管理员角色访问管理页面
内容的提问来源于stack exchange,提问作者NicolasN
相关产品推荐
相关产品推荐

