.NET 8 Blazor WebAssembly自定义认证状态不更新问题求助
问题核心分析
- 服务端错误注册了客户端专属的
CustomAuthenticationStateProvider,导致依赖缺失(服务端无ISessionStorageService)启动失败 - 客户端认证状态处理存在SessionStorage键大小写不匹配、ClaimsIdentity未指定认证类型的问题,导致授权组件无法识别已登录状态
1. 修正服务端Program.cs配置
服务端的JWT认证仅用于保护API接口,无需注册客户端的AuthenticationStateProvider,同时需使用完整的授权服务而非轻量版:
builder.Services.AddAuthentication(o => { o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o => { o.RequireHttpsMetadata = false; o.SaveToken = true; o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JwtAuthenticationManager.JWT_SECURITY_KEY)), ValidateAudience = false }; }); builder.Services.AddSingleton<UserAccountService>(); // 替换AddAuthorizationCore为服务端专用的AddAuthorization builder.Services.AddAuthorization(); // 移除客户端专属的AuthenticationStateProvider注册 // builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>(); // 服务端无需BlazoredSessionStorage,可移除 // builder.Services.AddBlazoredSessionStorage();
2. 修复客户端CustomAuthenticationStateProvider问题
问题1:SessionStorage键大小写不匹配
统一键名避免大小写差异导致的读取失败;
问题2:ClaimsIdentity未指定认证类型
只有带认证类型的ClaimsIdentity才会被Blazor授权系统识别为已登录状态。
修改后的完整代码:
public class CustomAuthenticationStateProvider : AuthenticationStateProvider { private readonly ISessionStorageService _sessionStorage; private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity()); // 统一SessionStorage键名 private const string SessionKey = "userSession"; public CustomAuthenticationStateProvider(ISessionStorageService sessionStorage) { _sessionStorage = sessionStorage; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { try { var userSession = await _sessionStorage.ReadEncryptedItemAsync<UserSession>(SessionKey); if (userSession == null) return new AuthenticationState(_anonymous); var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.Name, userSession.UserName), new Claim(ClaimTypes.NameIdentifier, userSession.Id.ToString()) }, "JwtAuth")); return new AuthenticationState(claimsPrincipal); } catch { return new AuthenticationState(_anonymous); } } public async Task UpdateAuthenticationState(UserSession? userSession) { ClaimsPrincipal claimsPrincipal; if (userSession != null) { claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.Name, userSession.UserName), new Claim(ClaimTypes.NameIdentifier, userSession.Id.ToString()) }, "JwtAuth")); // 与GetAuthenticationStateAsync保持一致的认证类型 userSession.ExpiryTimeStamp = DateTime.Now.AddSeconds(userSession.ExpiresIn); await _sessionStorage.SaveItemEncryptedAsync(SessionKey, userSession); // 使用统一键名 } else { claimsPrincipal = _anonymous; await _sessionStorage.RemoveItemAsync(SessionKey); // 使用统一键名 } NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal))); } public async Task<string> GetToken() { var result = string.Empty; try { var userSession = await _sessionStorage.ReadEncryptedItemAsync<UserSession>(SessionKey); if (userSession != null && DateTime.Now < userSession.ExpiryTimeStamp) result = userSession.Token; } catch { } return result; } }
3. 确认客户端Program.cs配置
确保客户端仅注册必要服务:
builder.Services.AddBlazoredSessionStorage(); builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>(); builder.Services.AddAuthorizationCore(); // 若需调用API,确保注册HttpClient builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
4. 确认App.razor结构
确保应用根组件包含CascadingAuthenticationState,用于传递认证状态:
<CascadingAuthenticationState> <Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <p>你没有权限访问此页面,请登录。</p> </NotAuthorized> </AuthorizeRouteView> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>未找到</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p>抱歉,未找到该页面。</p> </LayoutView> </NotFound> </Router> </CascadingAuthenticationState>
内容的提问来源于stack exchange,提问作者Gorban Arseny
相关产品推荐
相关产品推荐

