Blazor WASM组件与控制器认证授权状态不一致问题求助
核心思路
让客户端的CustomAuthenticationStateProvider不再仅依赖LocalStorage缓存,而是通过调用服务端接口校验Cookie有效性,以此同步两端的认证状态。
1. 服务端新增会话校验接口
在服务端添加一个无需授权的接口,用来验证当前请求的Cookie是否有效,并返回用户基础信息:
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { [HttpGet("check-session")] public IActionResult CheckSession() { if (User.Identity?.IsAuthenticated == true) { return Ok(new { IsAuthenticated = true, UserName = User.Identity.Name, // 按需添加其他必要字段,比如角色、权限等 }); } return Ok(new { IsAuthenticated = false }); } }
2. 修改CustomAuthenticationStateProvider实现
重写认证状态获取逻辑,优先调用服务端校验接口,同步更新客户端缓存与认证状态:
using Microsoft.AspNetCore.Components.Authorization; using System.Security.Claims; using Blazored.LocalStorage; public class CustomAuthenticationStateProvider : AuthenticationStateProvider { private readonly HttpClient _httpClient; private readonly ILocalStorageService _localStorage; private readonly AuthenticationState _anonymousState = new(new ClaimsPrincipal(new ClaimsIdentity())); public CustomAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage) { _httpClient = httpClient; _localStorage = localStorage; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { try { // 调用服务端会话校验接口 var sessionResult = await _httpClient.GetFromJsonAsync<SessionValidationResult>("api/auth/check-session"); if (sessionResult?.IsAuthenticated == true) { // 构造已认证的ClaimsPrincipal var claims = new List<Claim> { new Claim(ClaimTypes.Name, sessionResult.UserName) // 可根据返回结果添加角色、权限等Claim }; var identity = new ClaimsIdentity(claims, "CookieAuthentication"); var authenticatedUser = new ClaimsPrincipal(identity); // 同步更新LocalStorage,保持客户端缓存与服务端一致 await _localStorage.SetItemAsync("currentUser", sessionResult); return new AuthenticationState(authenticatedUser); } else { // Cookie已过期,清空客户端缓存 await _localStorage.RemoveItemAsync("currentUser"); return _anonymousState; } } catch (Exception) { // 网络异常时,降级使用LocalStorage缓存(建议添加用户提示) var cachedUser = await _localStorage.GetItemAsync<SessionValidationResult>("currentUser"); if (cachedUser?.IsAuthenticated == true) { var claims = new List<Claim> { new Claim(ClaimTypes.Name, cachedUser.UserName) }; var identity = new ClaimsIdentity(claims, "CookieAuthentication"); return new AuthenticationState(new ClaimsPrincipal(identity)); } return _anonymousState; } } // 提供主动更新认证状态的方法,供外部触发 public async Task RefreshAuthenticationState() { NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } } // 用于接收服务端返回的会话校验结果 public class SessionValidationResult { public bool IsAuthenticated { get; set; } public string UserName { get; set; } }
3. 主动触发状态同步
为了避免客户端状态滞后,可在以下场景主动调用状态刷新:
- 页面导航时:在
MainLayout的OnInitializedAsync中添加定时检查(比Cookie过期时间早5分钟) - 用户操作敏感功能前:比如点击需要授权的按钮时
示例定时刷新实现:
@inject AuthenticationStateProvider AuthStateProvider @code { protected override async Task OnInitializedAsync() { // 每25分钟刷新一次认证状态(Cookie过期时间设为30分钟) using var timer = new PeriodicTimer(TimeSpan.FromMinutes(25)); while (await timer.WaitForNextTickAsync()) { await ((CustomAuthenticationStateProvider)AuthStateProvider).RefreshAuthenticationState(); } } }
4. 登出逻辑同步
客户端登出时,不仅要清空LocalStorage,还要调用服务端登出接口清除Cookie:
// 客户端登出方法 public async Task Logout() { // 调用服务端登出接口 await _httpClient.PostAsync("api/auth/logout", null); // 清空客户端缓存并更新认证状态 await _localStorage.RemoveItemAsync("currentUser"); await ((CustomAuthenticationStateProvider)AuthStateProvider).RefreshAuthenticationState(); } // 服务端登出接口 [HttpPost("logout")] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Ok(); }
内容的提问来源于stack exchange,提问作者Stavros Koureas
相关产品推荐
相关产品推荐

