.NET 8 Blazor WASM直接访问URL触发401错误,求排查
问题分析与解决方案
核心问题根源
直接输入URL或刷新页面时出现401,本质是服务器端中间件顺序错误导致路由请求被提前拦截,同时CustomAuthStateProvider未在页面初始化时正确加载认证状态,导致客户端授权逻辑无法生效:
- 服务器端
UseAuthorization()中间件会拦截所有请求(包括Blazor页面的初始加载请求),但Blazor页面的授权应该由客户端AuthorizeRouteView处理,而非服务器直接返回401。 - 刷新页面时
CustomAuthStateProvider未触发,说明初始化阶段未从本地存储加载Token,客户端认证状态未更新。
具体修复步骤
1. 调整服务器端中间件顺序
修改服务器项目Program.cs,让授权逻辑仅作用于API控制器,不拦截Blazor页面请求:
var app = builder.Build(); // ... 环境配置代码保持不变 ... app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); // 仅对API控制器应用授权检查 app.MapControllers().RequireAuthorization(); app.UseAntiforgery(); // Blazor路由不启用服务器端授权,交给客户端处理 app.MapRazorComponents<App>() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(MyWeb.Client._Imports).Assembly); app.Run();
2. 修复CustomAuthStateProvider初始化逻辑
确保页面刷新时,能从本地存储加载Token并更新认证状态:
public class CustomAuthStateProvider : AuthenticationStateProvider { private readonly ILocalStorageService _localStorage; private readonly HttpClient _httpClient; public CustomAuthStateProvider(ILocalStorageService localStorage, HttpClient httpClient) { _localStorage = localStorage; _httpClient = httpClient; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { // 初始化时从本地存储读取Token var token = await _localStorage.GetItemAsync<string>("authToken"); var identity = new ClaimsIdentity(); if (!string.IsNullOrEmpty(token)) { // 解析JWT并创建ClaimsIdentity(根据你的Token格式调整) identity = new ClaimsIdentity(ParseJwtClaims(token), "jwt"); // 自动给HttpClient附加Token _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); } return new AuthenticationState(new ClaimsPrincipal(identity)); } // JWT解析辅助方法 private IEnumerable<Claim> ParseJwtClaims(string jwt) { var payload = jwt.Split('.')[1]; var jsonBytes = Convert.FromBase64String(payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=')); var claims = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes); return claims.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())); } }
3. 配置HttpClient自动附加Token
修改客户端Program.cs,让API请求自动携带Token:
var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.Services.AddAuthorizationCore(); builder.Services.AddCascadingAuthenticationState(); // 配置带授权处理的命名HttpClient builder.Services.AddHttpClient("MyWeb.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler<AuthorizationMessageHandler>(); // 注册授权消息处理器 builder.Services.AddScoped<AuthorizationMessageHandler>(sp => { var handler = sp.GetRequiredService<AuthorizationMessageHandler>() .ConfigureHandler( authorizedUrls: new[] { builder.HostEnvironment.BaseAddress }, scopes: Array.Empty<string>()); return handler; }); // 使用命名HttpClient替代默认实例 builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("MyWeb.ServerAPI")); builder.Services.AddBlazoredLocalStorage(); builder.Services.AddSingleton<AuthenticationStateProvider, CustomAuthStateProvider>(); builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); await builder.Build().RunAsync();
4. 完善Routes.razor的导航注入
确保NavigationManager正确注入,保证未授权时的重定向逻辑生效:
@inject NavigationManager navManager <Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"> <NotAuthorized> @{ var returnUrl = navManager.ToBaseRelativePath(navManager.Uri); <RedirectToLogin ReturnUrl="@returnUrl" /> } </NotAuthorized> </AuthorizeRouteView> <FocusOnNavigate RouteData="routeData" Selector="h1" /> </Found> <NotFound> <LayoutView Layout="typeof(Layout.MainLayout)"> <p>抱歉,当前地址不存在内容。</p> </LayoutView> </NotFound> </Router>
总结
- 服务器端仅对API请求做授权检查,Blazor页面的授权逻辑完全交给客户端处理。
- 确保
CustomAuthStateProvider在页面初始化时加载本地Token,刷新页面时能正确识别认证状态。 - 配置HttpClient自动附加Token,避免API请求出现401错误。
内容的提问来源于stack exchange,提问作者coolblue2000
相关产品推荐
相关产品推荐

