Blazor Server设置forceLoad:true导航时触发IAuthenticationService错误
问题背景
开发一款展示数据库数据的内部工具,通过自定义CustomAuthStateProvider管理身份验证,支持登录、注销功能,权限限制逻辑运行正常。
触发场景与错误
使用以下导航代码时,仅当设置forceLoad: true会导致应用崩溃:
NavigationManager.NavigateTo($"/myPage/dateFrom={System.Uri.EscapeUriString(fromStr)}/dateTo={System.Uri.EscapeUriString(toStr)}", forceLoad: true);
错误提示:
InvalidOperationException: 找不到所需的'IAuthenticationService'服务。请在应用启动代码中调用'IServiceCollection.AddAuthentication'添加所有必需的服务。
额外发现:手动在浏览器地址栏输入应用URL并回车刷新时,会出现相同错误。
后续尝试与新错误
重构部分身份验证逻辑后,出现新错误:
可通过AddAuthentication(string defaultScheme)或AddAuthentication(Action
configureOptions)设置默认方案。
尝试过相关解决方案但未解决问题。
相关代码
CustomAuthStateProvider.cs
using Microsoft.AspNetCore.Components.Authorization; using System.Security.Claims; using System.Threading.Tasks; public class CustomAuthStateProvider : AuthenticationStateProvider { public CustomAuthStateProvider() { this.CurrentUser = this.GetAnonymous(); } private ClaimsPrincipal CurrentUser { get; set; } private ClaimsPrincipal GetUser(string userName, uint id, string role) { var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Sid, id.ToString(new System.Globalization.CultureInfo("de-DE"))), new Claim(ClaimTypes.Name, userName), new Claim(ClaimTypes.Role, role) }, "Authentication type"); return new ClaimsPrincipal(identity); } private ClaimsPrincipal GetAnonymous() { var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Sid, "0"), new Claim(ClaimTypes.Name, "Anonymous"), new Claim(ClaimTypes.Role, "Anonymous") }, null); return new ClaimsPrincipal(identity); } public override Task<AuthenticationState> GetAuthenticationStateAsync() { var task = Task.FromResult(new AuthenticationState(this.CurrentUser)); return task; } public Task<AuthenticationState> ChangeUser(string username, uint id, string role) { this.CurrentUser = this.GetUser(username, id, role); var task = this.GetAuthenticationStateAsync(); this.NotifyAuthenticationStateChanged(task); return task; } public Task<AuthenticationState> Logout() { this.CurrentUser = this.GetAnonymous(); var task = this.GetAuthenticationStateAsync(); this.NotifyAuthenticationStateChanged(task); return task; } }
Program.cs
var builder = WebApplication.CreateBuilder(args); // Add MudBlazor services builder.Services.AddMudServices(); // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); builder.Services.AddSingleton<InitializationStateService>(); // Logging builder.Services.AddScoped<CustomAuthStateProvider>(); builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthStateProvider>()); builder.Services.AddAuthorizationCore(); builder.Services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(0); options.DisconnectedCircuitMaxRetained = 0; }); builder.Services.AddHealthChecks(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); } app.UseStaticFiles(); app.UseAntiforgery(); app.UseHealthChecks("/health"); app.MapRazorComponents<App>() .AddInteractiveServerRenderMode(); app.Run();
解决方案
问题根源:当使用forceLoad: true或手动刷新页面时,会触发服务器端初始渲染,此时Blazor会尝试调用ASP.NET Core的身份验证中间件,但当前仅注册了AddAuthorizationCore(仅提供授权逻辑,无身份验证服务),缺少IAuthenticationService的实现。
修复步骤
添加身份验证服务并设置默认方案
修改Program.cs中的服务注册代码,将builder.Services.AddAuthorizationCore();替换为:// 添加身份验证服务并设置默认方案 builder.Services.AddAuthentication(options => { options.DefaultScheme = "CustomScheme"; }) // 添加空的自定义身份验证处理程序 .AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>("CustomScheme", options => { }); // 保留授权服务注册 builder.Services.AddAuthorizationCore();创建自定义身份验证处理程序
新增CustomAuthenticationHandler.cs文件:using Microsoft.AspNetCore.Authentication; using System.Security.Claims; using System.Threading.Tasks; public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public CustomAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // 由于使用CustomAuthStateProvider管理用户状态,此处返回失败即可 return Task.FromResult(AuthenticateResult.Fail("无身份验证操作")); } }调整中间件顺序
在Program.cs的请求管道中添加UseAuthentication中间件:app.UseStaticFiles(); app.UseAuthentication(); // 添加此中间件 app.UseAntiforgery();
原理说明
AddAuthentication会注册IAuthenticationService,解决找不到服务的错误;- 空的自定义处理程序用于满足ASP.NET Core身份验证框架的要求,实际用户状态仍由
CustomAuthStateProvider管理; - 中间件顺序确保身份验证服务在请求管道中提前初始化。
内容的提问来源于stack exchange,提问作者Daniel

