.NET 7托管Blazor WASM自定义AuthenticationStateProvider致HttpClient崩溃
核心问题分析
错误的本质是类型强制转换失败,并非循环依赖。在.NET 7 Blazor WebAssembly独立账户认证方案中,框架默认注册的AuthenticationStateProvider实际是RemoteAuthenticationService<RemoteAuthenticationState, RemoteUserAccount, ApiAuthorizationProviderOptions>的实例——这个类是框架实现远程认证逻辑的核心,而BaseAddressAuthorizationMessageHandler内部需要将注入的AuthenticationStateProvider强制转换为该具体类型来获取认证令牌。
当你用自定义的CustomStateProvider直接替换默认的AuthenticationStateProvider后,消息处理器尝试将CustomStateProvider实例转换为RemoteAuthenticationService类型,自然会抛出InvalidCastException。而public HttpClient不依赖认证消息处理器,因此不受影响。
解决方案
方案一:继承RemoteAuthenticationService(推荐)
如果需要扩展原有远程认证逻辑,正确的做法是继承框架提供的RemoteAuthenticationService而非直接继承AuthenticationStateProvider:
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.Extensions.Options; using System.Security.Claims; namespace SiteBuddy.Client.Services { public class CustomStateProvider : RemoteAuthenticationService<RemoteAuthenticationState, RemoteUserAccount, ApiAuthorizationProviderOptions> { public CustomStateProvider( IRemoteAuthenticationService<RemoteAuthenticationState, RemoteUserAccount, ApiAuthorizationProviderOptions> innerService, HttpClient httpClient, IOptions<ApiAuthorizationProviderOptions> options, NavigationManager navigationManager, ILoggerFactory loggerFactory) : base(innerService, httpClient, options, navigationManager, loggerFactory) { } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { // 先调用框架原有逻辑获取基础认证状态 var baseState = await base.GetAuthenticationStateAsync(); // 此处添加自定义逻辑,比如修改Claims、扩展认证状态判断等 if (baseState.User.Identity?.IsAuthenticated ?? false) { var modifiedClaims = new List<Claim>(baseState.User.Claims); modifiedClaims.Add(new Claim("custom_claim", "custom_value")); var newIdentity = new ClaimsIdentity(modifiedClaims, baseState.User.Identity.AuthenticationType); return new AuthenticationState(new ClaimsPrincipal(newIdentity)); } return baseState; } // 可按需重写其他受保护方法,如HandleLoginAsync、HandleLogoutAsync等 } }
注册代码修改为:
// 注册自定义状态提供者 builder.Services.AddScoped<CustomStateProvider>(); // 将AuthenticationStateProvider替换为自定义实现 builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomStateProvider>()); // 注:独立账户模板默认已添加AddApiAuthorization,无需重复注册
方案二:装饰器模式(轻量扩展)
如果仅需在原有认证逻辑基础上添加少量自定义处理,可采用装饰器模式包裹默认的AuthenticationStateProvider:
using Microsoft.AspNetCore.Components.Authorization; namespace SiteBuddy.Client.Services { public class CustomStateProvider : AuthenticationStateProvider { private readonly AuthenticationStateProvider _innerProvider; public CustomStateProvider(AuthenticationStateProvider innerProvider) { _innerProvider = innerProvider; // 订阅内部提供者的状态变化并转发 _innerProvider.AuthenticationStateChanged += (task) => NotifyAuthenticationStateChanged(task); } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { var state = await _innerProvider.GetAuthenticationStateAsync(); // 添加自定义逻辑,比如日志记录、状态修改等 return state; } } }
注册代码修改为:
// 用装饰器包裹默认的AuthenticationStateProvider builder.Services.AddScoped<CustomStateProvider>(sp => new CustomStateProvider(sp.GetRequiredService<AuthenticationStateProvider>())); // 替换全局AuthenticationStateProvider为装饰器实例 builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomStateProvider>());
内容的提问来源于stack exchange,提问作者Simon Bailey

