You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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获取的用户唯一标识(如NameIdentifier Claim值)能和数据库中的权限记录正确绑定。

内容的提问来源于stack exchange,提问作者Stephen Pefanis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 12:42:22