Blazor:Windows认证后在WhenAuthenticated事件调用自定义AuthenticationStateProvider
解决方案
核心思路
在Windows认证完成的事件中,通过依赖注入获取自定义AuthenticationStateProvider实例,执行自定义授权流程后更新ClaimsPrincipal并通知Blazor组件状态变更,以此实现基于角色的授权。
步骤1:改造自定义Negotiate事件类,注入AuthenticationStateProvider
修改你的CMAPNegotiateEvents,添加自定义AuthenticationStateProvider的构造函数注入,在认证完成事件中执行自定义授权逻辑:
public class CMAPNegotiateEvents : NegotiateEvents { private readonly IWebHostEnvironment _env; private readonly CustomAuthenticationStateProvider _customAuthStateProvider; // 构造函数注入自定义AuthenticationStateProvider public CMAPNegotiateEvents(IWebHostEnvironment env, CustomAuthenticationStateProvider customAuthStateProvider) { _env = env; _customAuthStateProvider = customAuthStateProvider; } // 对应Windows认证完成的事件(替代你提到的WhenAuthenticated) public override async Task AuthenticateResultReceived(AuthenticateResultReceivedContext context) { if (context.Result.Succeeded) { // 1. 获取Windows认证后的基础ClaimsPrincipal var windowsPrincipal = context.Principal; // 2. 执行自定义应用授权流程,比如调用内部服务获取角色 var authorizedPrincipal = await RunCustomAuthorization(windowsPrincipal); // 3. 调用自定义Provider更新Principal并通知Blazor await _customAuthStateProvider.UpdateAuthenticatedPrincipal(authorizedPrincipal); // 可选:更新HttpContext的Principal,供后续中间件使用 context.Principal = authorizedPrincipal; } } // 自定义授权流程实现(替换为你的实际逻辑) private async Task<ClaimsPrincipal> RunCustomAuthorization(ClaimsPrincipal windowsPrincipal) { var windowsUsername = windowsPrincipal.Identity?.Name; if (string.IsNullOrEmpty(windowsUsername)) throw new InvalidOperationException("无法获取Windows认证用户名"); // 调用你的自定义认证服务获取角色列表 var userRoles = await FetchRolesFromCustomAuthSystem(windowsUsername); // 基于Windows身份创建新的ClaimsIdentity,添加自定义角色 var customIdentity = new ClaimsIdentity(windowsPrincipal.Identity); foreach (var role in userRoles) { customIdentity.AddClaim(new Claim(ClaimTypes.Role, role)); } return new ClaimsPrincipal(customIdentity); } // 模拟调用自定义认证服务的方法 private async Task<List<string>> FetchRolesFromCustomAuthSystem(string username) { // 替换为你的实际接口调用逻辑 return await Task.FromResult(new List<string> { "Admin", "ContentEditor" }); } }
步骤2:完善自定义AuthenticationStateProvider
确保你的自定义Provider包含更新Principal并通知状态变更的方法:
public class CustomAuthenticationStateProvider : AuthenticationStateProvider { private ClaimsPrincipal _currentPrincipal = new ClaimsPrincipal(new ClaimsIdentity()); public override Task<AuthenticationState> GetAuthenticationStateAsync() { return Task.FromResult(new AuthenticationState(_currentPrincipal)); } // 自定义方法:更新Principal并触发Blazor状态变更 public async Task UpdateAuthenticatedPrincipal(ClaimsPrincipal newPrincipal) { _currentPrincipal = newPrincipal; // 通知所有监听认证状态的组件更新 NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } }
步骤3:在Program.cs中正确注册服务
确保自定义Provider和Negotiate事件的依赖注入配置正确:
// 注册自定义AuthenticationStateProvider builder.Services.AddScoped<CustomAuthenticationStateProvider>(); // 替换默认的AuthenticationStateProvider为自定义实现 builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomAuthenticationStateProvider>()); // 配置Windows认证,通过DI获取CMAPNegotiateEvents实例 builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(options => { // 使用工厂模式获取事件实例,确保依赖注入生效 options.Events = sp => new CMAPNegotiateEvents(builder.Environment, sp.GetRequiredService<CustomAuthenticationStateProvider>()); }); // 启用授权中间件 builder.Services.AddAuthorization();
关键注意点
- 事件选择:使用
AuthenticateResultReceived事件而非自定义WhenAuthenticated,这是Negotiate认证流程中官方提供的、Windows认证完成后的回调点。 - 状态通知:必须调用
NotifyAuthenticationStateChanged,否则Blazor组件无法感知认证状态变化,基于角色的AuthorizeView等授权组件不会生效。 - 身份保留:基于Windows认证的ClaimsIdentity扩展角色,而非完全替换,既保留Windows认证的合法性,又添加自定义授权信息。
内容的提问来源于stack exchange,提问作者Johnny Wu
相关产品推荐
相关产品推荐

