如何自定义Blazor Server Windows AuthenticationStateProvider
解决方案
你现在必须逐页注入自定义类才能让权限生效,本质原因是角色追加逻辑写在了自定义类的构造函数里——只有类被注入实例化时,逻辑才会执行,根本没接入Blazor原生的认证状态管线。
你之前自定义AuthenticationStateProvider拿不到用户名,是因为没有复用Windows认证默认生成的认证状态,完全不需要依赖HttpContext就能拿到当前登录用户信息,按以下步骤实现即可全应用自动生效权限逻辑,不需要逐页注入:
1. 编写自定义认证状态提供器
删掉原来的CompanyAuthentication类,新建自定义类继承自AuthenticationStateProvider,通过默认的ServerAuthenticationStateProvider获取Windows认证生成的原始用户身份,再追加自定义角色逻辑:
using Microsoft.AspNetCore.Components.Authorization; using System.Security.Claims; public class CustomRoleAuthStateProvider : AuthenticationStateProvider { private readonly ServerAuthenticationStateProvider _windowsAuthProvider; // 可在此注入数据库访问服务,用于查询用户角色 public CustomRoleAuthStateProvider(ServerAuthenticationStateProvider windowsAuthProvider) { _windowsAuthProvider = windowsAuthProvider; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { // 直接获取Windows认证生成的原始认证状态,自带域/用户名信息,无需依赖HttpContext var authState = await _windowsAuthProvider.GetAuthenticationStateAsync(); var user = authState.User; // 匿名用户直接返回原始状态,不做处理 if (user.Identity is not { IsAuthenticated: true }) { return authState; } string username = user.Identity.Name!; // 替换为从数据库/配置文件查询用户角色的实际逻辑 var roles = new List<string>(); if (username.StartsWith("admin", StringComparison.OrdinalIgnoreCase)) { roles.Add("admin"); } // 追加角色声明到现有用户身份 var identity = (ClaimsIdentity)user.Identity; foreach (var role in roles) { identity.AddClaim(new Claim(ClaimTypes.Role, role)); } // 通知认证状态更新,触发UI授权刷新 NotifyAuthenticationStateChanged(Task.FromResult(authState)); return authState; } }
2. 注册自定义服务替换默认实现
修改Startup.cs(.NET 6+为Program.cs)的服务注册代码,删除原来的services.AddScoped<CompanyAuthentication>();,添加如下注册代码,注意放在Windows认证和Blazor基础服务注册之后:
// 原有Windows认证相关注册保留即可,不需要修改 // services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate(); // services.AddAuthorization(); // services.AddRazorPages(); // services.AddServerSideBlazor(); // 注册自定义认证状态提供器,替换默认实现 services.AddScoped<AuthenticationStateProvider>(sp => { var defaultWindowsAuthProvider = sp.GetRequiredService<ServerAuthenticationStateProvider>(); return new CustomRoleAuthStateProvider(defaultWindowsAuthProvider); });
3. 清理冗余代码
删除所有Razor页面中@inject CompanyAuthentication auth的注入代码,不需要做其他修改,原有的<AuthorizeView>组件、[Authorize]特性等所有原生授权逻辑,都会自动读取追加了角色的用户信息,全应用自动生效。
注意事项
- 不要在Blazor代码中使用
.Result、.Wait()这类同步阻塞方式调用异步方法,很容易触发线程死锁,自定义提供器中全程使用await处理异步调用即可。 - 用户名判断建议加上
StringComparison.OrdinalIgnoreCase忽略大小写,避免域账号大小写差异导致角色判断失效。 - 如果系统运行过程中用户角色会发生变更,可以在角色更新逻辑中注入
AuthenticationStateProvider,强转为CustomRoleAuthStateProvider后主动调用NotifyAuthenticationStateChanged方法,UI会自动刷新权限状态,不需要用户重新登录。 - 不要使用
HttpContext.Current,Blazor Server中HttpContext仅在初始HTTP请求阶段可用,SignalR建立连接后HttpContext就会释放,依赖它的逻辑会出现随机失效问题。
内容的提问来源于stack exchange,提问作者Ahmad
相关产品推荐
相关产品推荐

