Blazor Server中基于角色/权限的授权实现问题求助
Blazor Server 角色&权限访问控制解决方案
核心思路
基于自定义AppUserContext存储用户角色与权限,替代依赖HttpContext的传统授权体系,分别实现页面级角色管控、组件级权限管控、服务端方法权限校验。
1. 定义用户上下文服务
创建Scoped服务存储当前用户的角色与权限,认证时从数据库加载数据:
public class AppUserContext { public string UserId { get; set; } public List<string> Roles { get; set; } = new(); public List<string> Permissions { get; set; } = new(); // 角色校验 public bool IsInRole(string role) => Roles.Contains(role, StringComparer.OrdinalIgnoreCase); // 权限校验 public bool HasPermission(string permission) => Permissions.Contains(permission, StringComparer.OrdinalIgnoreCase); }
在Program.cs注册为Scoped服务:
builder.Services.AddScoped<AppUserContext>();
2. 页面级角色授权(替代[Authorize])
创建页面基类,在初始化时校验用户角色,无权限则跳转至未授权页面:
public class AuthorizePageBase : ComponentBase { [Inject] protected AppUserContext UserContext { get; set; } [Inject] protected NavigationManager NavigationManager { get; set; } [Parameter] public string AllowedRoles { get; set; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); var allowedRoles = AllowedRoles.Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(r => r.Trim()) .ToList(); // 检查是否拥有任一允许角色 var hasAccess = allowedRoles.Any(role => UserContext.IsInRole(role)); if (!hasAccess) { NavigationManager.NavigateTo("/unauthorized"); } } }
页面使用示例:
@inherits AuthorizePageBase @{ AllowedRoles = "SuperAdmin, Auditor, Operator"; } <!-- 页面内容 -->
3. 实现CustomAuthorizeView组件
创建自定义组件,根据权限控制子内容渲染:
@inject AppUserContext UserContext @if (HasRequiredPermission) { @ChildContent } else { @UnauthorizedContent } @code { [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public RenderFragment UnauthorizedContent { get; set; } [Parameter] public string Permission { get; set; } private bool HasRequiredPermission => !string.IsNullOrWhiteSpace(Permission) && UserContext.HasPermission(Permission); protected override void OnParametersSet() { base.OnParametersSet(); if (string.IsNullOrWhiteSpace(Permission)) { throw new ArgumentNullException(nameof(Permission), "权限标识不能为空"); } } }
组件使用示例(与你的需求完全匹配):
<CustomAuthorizeView Permission="AddUser"> <div class="radzen-filter"> <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem" Style="margin-bottom: 1rem;"> <RadzenButton Click="@CreateUser" Text="Add User" Icon="add" ButtonStyle="ButtonStyle.Primary" /> </RadzenStack> </div> </CustomAuthorizeView>
如需自定义未授权提示:
<CustomAuthorizeView Permission="AddUser"> <!-- 授权可见内容 --> <UnauthorizedContent> <p>您没有添加用户的权限</p> </UnauthorizedContent> </CustomAuthorizeView>
4. 服务端方法权限校验
在业务服务中注入AppUserContext,直接校验权限:
public class UserService { private readonly AppUserContext _userContext; public UserService(AppUserContext userContext) { _userContext = userContext; } public async Task CreateUser(UserDto userDto) { if (!_userContext.HasPermission("AddUser")) { throw new UnauthorizedAccessException("无添加用户权限"); } // 业务逻辑实现 } }
5. 认证时加载角色与权限
在自定义认证逻辑中,从数据库加载用户角色及关联权限,填充至AppUserContext:
public async Task<bool> AuthenticateAsync(string username, string password) { // 1. 校验用户名密码 var user = await _userRepo.GetUserByUsernameAsync(username); if (user == null || !VerifyPassword(password, user.PasswordHash)) { return false; } // 2. 加载角色与权限 var roles = await _roleRepo.GetRolesByUserIdAsync(user.Id); var permissions = await _permissionRepo.GetPermissionsByRoleIds(roles.Select(r => r.Id)); // 3. 填充至用户上下文 var userContext = _serviceProvider.GetRequiredService<AppUserContext>(); userContext.UserId = user.Id; userContext.Roles = roles.Select(r => r.Name).ToList(); userContext.Permissions = permissions.Select(p => p.Name).ToList(); return true; }
常见问题排查
如果你的示例方案无法运行,优先检查以下几点:
AppUserContext是否注册为Scoped服务,且认证时正确填充了角色/权限数据CustomAuthorizeView是否正确注入AppUserContext- 角色/权限名称是否与数据库中存储的一致(注意大小写问题)
- 页面基类的
AllowedRoles参数是否正确传递
内容的提问来源于stack exchange,提问作者Petar Percuklieski
相关产品推荐
相关产品推荐

