You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:34:57