Blazor Server(.NET7)基于用户菜单权限的页面访问控制问询
Blazor Server .NET7 自定义页面权限校验方案
针对你这种基于数据库菜单权限的场景,不用局限于角色授权,下面几个可落地的方案供你参考:
一、自定义授权策略(推荐,贴合ASP.NET Core授权体系)
1. 定义权限需求与处理器
先创建自定义授权需求类,用来标记需要校验页面权限的场景:
public class PagePermissionRequirement : IAuthorizationRequirement { public string PageUrl { get; } public PagePermissionRequirement(string pageUrl) { PageUrl = pageUrl; } }
再实现授权处理器,这里假设你已经封装了IPermissionService(Scoped服务)用来获取当前用户的允许访问页面列表:
public class PagePermissionHandler : AuthorizationHandler<PagePermissionRequirement> { private readonly IPermissionService _permissionService; public PagePermissionHandler(IPermissionService permissionService) { _permissionService = permissionService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PagePermissionRequirement requirement) { // 获取当前用户有权访问的页面URL列表 var allowedPages = await _permissionService.GetAllowedPageUrlsAsync(context.User); if (allowedPages.Contains(requirement.PageUrl, StringComparer.OrdinalIgnoreCase)) { context.Succeed(requirement); } } }
2. 注册授权服务
在Program.cs中注册自定义处理器和策略:
builder.Services.AddScoped<IPermissionService, PermissionService>(); builder.Services.AddAuthorization(options => { options.AddPolicy("PagePermission", policy => policy.Requirements.Add(new PagePermissionRequirement(string.Empty))); }); builder.Services.AddScoped<IAuthorizationHandler, PagePermissionHandler>();
3. 页面中使用
在需要校验的页面组件顶部添加授权属性:
@page "/admin" @attribute [Authorize(Policy = "PagePermission")]
或者在AuthorizeView中指定策略控制UI:
<AuthorizeView Policy="PagePermission"> <Authorized> <!-- 有权限展示的内容 --> </Authorized> <NotAuthorized> <p>你没有访问该页面的权限</p> </NotAuthorized> </AuthorizeView>
二、自定义授权组件(灵活控制UI逻辑)
如果需要更灵活的UI渲染逻辑,可以封装一个PageAuthorizedView组件,内部直接校验当前页面权限:
@inject IPermissionService PermissionService @inject NavigationManager NavManager @inject AuthenticationStateProvider AuthenticationStateProvider @code { private bool IsAuthorized { get; set; } protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; var currentPageUrl = NavManager.ToBaseRelativePath(NavManager.Uri); IsAuthorized = await PermissionService.IsPageAllowedAsync(user, currentPageUrl); } } @if (IsAuthorized) { @ChildContent } else { @NotAuthorizedContent ?? <p>无访问权限,请联系管理员</p> } [Parameter] public RenderFragment? ChildContent { get; set; } [Parameter] public RenderFragment? NotAuthorizedContent { get; set; }
使用方式:
<PageAuthorizedView> <!-- 页面核心内容 --> </PageAuthorizedView>
三、全局路由拦截(自动校验所有页面)
如果不想给每个页面单独加授权属性,可以自定义RouteView实现全局路由拦截:
public class AuthorizedRouteView : RouteView { [Inject] public IPermissionService PermissionService { get; set; } = default!; [Inject] public NavigationManager NavManager { get; set; } = default!; [Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = default!; protected override async Task RenderAsync(RenderTreeBuilder builder) { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; var currentPageUrl = NavManager.ToBaseRelativePath(NavManager.Uri); if (user.Identity?.IsAuthenticated == true) { var isAllowed = await PermissionService.IsPageAllowedAsync(user, currentPageUrl); if (!isAllowed) { // 跳转到无权限提示页或首页 NavManager.NavigateTo("/access-denied"); return; } } // 权限校验通过,正常渲染页面 await base.RenderAsync(builder); } }
然后在App.razor中替换默认的RouteView:
<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <AuthorizedRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
关键注意事项
- 权限缓存:用户的权限列表建议在登录后缓存(比如存入Claims、Scoped服务或Session),避免每次校验都查询数据库。
- 权限同步:如果用户权限在后台被修改,需要及时刷新权限缓存(比如强制用户重新登录)。
- AzureAD用户关联:确保从AzureAD获取的用户唯一标识(如
NameIdentifierClaim值)能和数据库中的权限记录正确绑定。
内容的提问来源于stack exchange,提问作者Stephen Pefanis
相关产品推荐
相关产品推荐

