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

Blazor Server应用如何基于用户名实现页面授权管控

Blazor Server Windows认证下基于用户名的授权实现方案

核心逻辑:Windows身份验证仍由IIS/系统层完成,只在应用授权层增加用户名白名单校验,不在列表内的已认证用户直接被撤销所有页面访问权限,全程不依赖角色体系。

1. 配置用户名白名单

在appsettings.json中维护允许访问的用户名列表,注意Windows认证返回的用户名带域/机器名前缀,必须写完整格式:

{
  "AllowedUserSettings": {
    "WhiteList": [
      "COMPANY-DOMAIN\\zhang.san",
      "COMPANY-DOMAIN\\li.si",
      "LOCAL-MACHINE\\dev-account",
      "IIS APPPOOL\\BlazorAppProd" // 必须加入IIS应用池对应的内置账号,避免部署后权限异常
    ]
  }
}

如果开启了配置热更新,后续增删白名单用户不需要重启应用即可生效。

2. 实现自定义用户名授权逻辑

不需要引入第三方权限库,写两个基础类完成校验:
首先定义授权要求:

// UsernameRequirement.cs
using Microsoft.AspNetCore.Authorization;

public class UsernameWhitelistRequirement : IAuthorizationRequirement
{
    public IReadOnlyList<string> AllowedUsernames { get; }
    public UsernameWhitelistRequirement(List<string> allowedUsernames)
    {
        AllowedUsernames = allowedUsernames.AsReadOnly();
    }
}

然后实现对应的授权处理器:

// UsernameWhitelistHandler.cs
using Microsoft.AspNetCore.Authorization;

public class UsernameWhitelistHandler : AuthorizationHandler<UsernameWhitelistRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UsernameWhitelistRequirement requirement)
    {
        var currentUser = context.User.Identity;
        // 未认证直接失败
        if (currentUser == null || !currentUser.IsAuthenticated || string.IsNullOrWhiteSpace(currentUser.Name))
        {
            context.Fail();
            return Task.CompletedTask;
        }
        // 忽略大小写匹配白名单,Windows用户名本身大小写不敏感
        if (requirement.AllowedUsernames.Contains(currentUser.Name, StringComparer.OrdinalIgnoreCase))
        {
            context.Succeed(requirement);
        }
        else
        {
            // 不在白名单直接拒绝,不进入后续其他授权校验
            context.Fail();
        }
        return Task.CompletedTask;
    }
}

3. 注册服务配置全局授权策略

在Program.cs中注册服务,把白名单校验配置为全局默认策略,所有页面默认都会走校验:

var builder = WebApplication.CreateBuilder(args);

// 读取白名单配置
var whiteList = builder.Configuration
    .GetSection("AllowedUserSettings:WhiteList")
    .Get<List<string>>() ?? new List<string>();

// 注册自定义授权处理器
builder.Services.AddScoped<IAuthorizationHandler, UsernameWhitelistHandler>();

// 配置授权策略
builder.Services.AddAuthorization(options =>
{
    // 全局默认策略:必须通过Windows认证 + 必须在白名单内
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .AddRequirements(new UsernameWhitelistRequirement(whiteList))
        .RequireAuthenticatedUser()
        .Build();
    // 额外注册命名策略,供个别页面单独指定使用
    options.AddPolicy("WhitelistOnly", policy =>
    {
        policy.AddRequirements(new UsernameWhitelistRequirement(whiteList));
        policy.RequireAuthenticatedUser();
    });
});

// 后续按原有逻辑注册Razor组件、Blazor Server服务即可

部署注意:IIS站点必须开启Windows身份验证、关闭匿名身份验证,否则无法拿到正确的Windows用户名。

4. 页面级访问控制

全局策略默认对所有带路由的Razor组件生效,不需要额外加特性。如果需要单独配置特殊页面:

  • 对需要额外强调权限的页面,在顶部加特性明确标注:
    @attribute [Authorize(Policy = "WhitelistOnly")]
    
  • 对需要放开权限的页面(比如无权限提示页、健康检查页),加匿名访问特性:
    @attribute [AllowAnonymous]
    

在App.razor中配置无权限时的展示内容,不需要额外写跳转逻辑:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                <div class="alert alert-danger mt-5 text-center">
                    <h3>访问被拒绝</h3>
                    <p>您的账号不在系统允许访问的用户列表内,请联系管理员开通权限。</p>
                </div>
            </NotAuthorized>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p class="mt-5 text-center">抱歉,您访问的页面不存在。</p>
        </LayoutView>
    </NotFound>
</Router>

常见踩坑说明

  • 不要只写用户名不带域/机器前缀,比如Windows返回的用户名是DOMAIN\zhangsan,白名单只写zhangsan会100%匹配失败
  • 不要用大小写敏感的字符串匹配,Windows系统对用户名大小写不敏感,避免因为账号名大小写格式问题误拦截合法用户
  • 撤销用户权限只需要把对应用户名从白名单移除即可,不需要修改角色映射、用户状态等其他配置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:39:19