如何在C# Blazor Server应用中维持AuthenticationState有效性?
解决方案
Blazor的[Authorize]属性仅在路由导航、组件初始化阶段验证身份状态,后续UI交互(如按钮点击)不会自动重新校验身份。手动删除LocalStorage中的Token后,AuthenticationStateProvider未收到变更通知,导致组件仍认为用户处于授权状态,直到页面刷新。以下是几种通用解决方案:
1. 监听LocalStorage变化,自动同步身份状态
利用浏览器storage事件监听LocalStorage的修改(支持跨标签页同步),在自定义AuthenticationStateProvider中触发身份状态更新:
修改CustomAuthenticationStateProvider
private readonly ILocalStorageService _localStorage; private readonly ITokenValidator _tokenValidator; private readonly IJSRuntime _jsRuntime; private DotNetObjectReference<CustomAuthenticationStateProvider> _dotNetRef; public CustomAuthenticationStateProvider(ILocalStorageService localStorage, ITokenValidator tokenValidator, IJSRuntime jsRuntime) { _localStorage = localStorage; _tokenValidator = tokenValidator; _jsRuntime = jsRuntime; _dotNetRef = DotNetObjectReference.Create(this); // 注册storage事件监听 _ = _jsRuntime.InvokeVoidAsync("window.addEventListener", "storage", _dotNetRef.CreateMethodReference(nameof(HandleStorageChange))); } [JSInvokable] public async Task HandleStorageChange(StorageEvent eventArgs) { // 仅处理AuthToken的变更 if (eventArgs.Key == "AuthToken") { var authState = await GetAuthenticationStateAsync(); NotifyAuthenticationStateChanged(Task.FromResult(authState)); } } // 释放资源 public void Dispose() { _ = _jsRuntime.InvokeVoidAsync("window.removeEventListener", "storage", _dotNetRef.CreateMethodReference(nameof(HandleStorageChange))); _dotNetRef.Dispose(); }
补充说明:当前标签页修改LocalStorage不会触发storage事件,主动修改Token的逻辑已在UpdateAuthenticationState中处理;手动删除Token的场景可结合方案2,在交互前主动校验。
2. 封装授权按钮组件,统一身份校验逻辑
创建通用授权按钮组件,所有需要授权的交互都使用该组件,避免重复编写校验代码:
AuthButton.razor
<button @onclick="HandleAuthorizedClick" @attributes="AdditionalAttributes"> @ChildContent </button> @code { [Inject] private AuthenticationStateProvider AuthStateProvider { get; set; } [Inject] private NavigationManager NavigationManager { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public EventCallback OnClick { get; set; } [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> AdditionalAttributes { get; set; } private async Task HandleAuthorizedClick() { // 点击前主动校验身份状态 var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (!authState.User.Identity.IsAuthenticated) { // 未授权时跳转登录页或提示 NavigationManager.NavigateTo("/login"); return; } // 已授权则执行原点击逻辑 await OnClick.InvokeAsync(); } }
使用方式(替换Index.razor中的Button)
@page "/" @attribute [Authorize] <PageTitle>Index</PageTitle> <AuthButton Color="ButtonColor.Primary" OnClick="OnlyAuthButton">OnlyAuth</AuthButton> @code{ [Inject] protected ToastService ToastService { get; set; } private async Task OnlyAuthButton() { ToastService.Notify(new ToastMessage(ToastType.Danger, Loc["Some error occured"])); } }
3. 定期校验身份状态(适合高实时性场景)
扩展AuthorizeView组件,在组件渲染后定期校验身份状态,确保授权状态实时更新:
@inherits AuthorizeView @{ base.BuildRenderTree(__builder); } @code { [Inject] private AuthenticationStateProvider AuthStateProvider { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { // 每2秒校验一次身份状态(间隔可自定义) await Task.Delay(2000); var authState = await AuthStateProvider.GetAuthenticationStateAsync(); NotifyAuthenticationStateChanged(Task.FromResult(authState)); } await base.OnAfterRenderAsync(firstRender); } }
核心原理
Blazor的AuthenticationStateProvider默认会缓存身份状态,只有调用NotifyAuthenticationStateChanged才会通知所有依赖组件重新渲染授权状态。上述方案分别从自动监听变更、交互前置校验、定期主动校验三个维度解决手动删除Token后的授权状态不同步问题。
内容的提问来源于stack exchange,提问作者rat1borik
相关产品推荐
相关产品推荐

