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

自定义AuthenticationStateProvider使用LocalStorage的JS互操作错误解决

问题核心

你碰到的这两个错误,本质都是Blazor服务器端中JS互操作时机不匹配导致的:

  • 第一个错误:GetAuthenticationStateAsync会在应用启动、组件渲染前就被调用,哪怕禁用了预渲染,此时浏览器的JS运行环境还没准备好,直接调用ProtectedLocalStorage必然触发JS互操作失败。
  • 第二个错误:电路断开时强行调用JS互操作,此时根本无法访问浏览器的LocalStorage。
修复方案

1. 重构认证状态提供者的逻辑

别在GetAuthenticationStateAsync里直接读LocalStorage,先返回匿名身份,等JS环境就绪后再异步读取并更新认证状态:

public class CustomAuthenticationStateProviderService : AuthenticationStateProvider
{
    private readonly ClaimsPrincipal _anonymous = new(new ClaimsIdentity());
    private readonly ProtectedLocalStorage _localStorage;
    private ClaimsPrincipal _currentUser;

    public CustomAuthenticationStateProviderService(ProtectedLocalStorage localStorage)
    {
        _localStorage = localStorage;
        _currentUser = _anonymous;
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        // 先返回当前已知的身份(初始是匿名)
        return Task.FromResult(new AuthenticationState(_currentUser));
    }

    // 新增方法:异步读取LocalStorage并更新认证状态
    public async Task UpdateAuthenticationStateAsync()
    {
        try
        {
            var tokenResult = await _localStorage.GetAsync<string>("headerToken");
            var token = tokenResult.Success ? tokenResult.Value : null;

            if (string.IsNullOrWhiteSpace(token))
            {
                _currentUser = _anonymous;
            }
            else
            {
                var claims = JWTService.DecryptToken(token);
                _currentUser = claims != null ? SetClaimPrincipal(claims) : _anonymous;
            }

            // 通知Blazor框架认证状态已变更
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser)));
        }
        catch (InvalidOperationException)
        {
            // 捕获JS互操作不可用的异常(比如电路断开、JS环境未初始化)
            _currentUser = _anonymous;
        }
        catch (Exception)
        {
            // 其他异常也回退到匿名身份
            _currentUser = _anonymous;
        }
    }

    private ClaimsPrincipal SetClaimPrincipal(IEnumerable<Claim> claims)
    {
        // 保留你原来的ClaimsPrincipal构造逻辑
        return new ClaimsPrincipal(new ClaimsIdentity(claims, "JWT"));
    }
}

2. 在根组件中触发认证状态更新

找你的根组件(比如MainLayout.razor),在OnAfterRenderAsync里调用上面新增的UpdateAuthenticationStateAsync,确保JS环境准备好后再读LocalStorage:

@inject CustomAuthenticationStateProviderService AuthStateProvider
@rendermode @(new InteractiveServerRenderMode(false))

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 首次渲染后再读取LocalStorage更新认证状态
            await AuthStateProvider.UpdateAuthenticationStateAsync();
        }
        await base.OnAfterRenderAsync(firstRender);
    }
}

3. 处理业务逻辑依赖认证状态的场景

如果你的页面需要在OnInitialized里用到认证状态,别直接同步拿结果,而是监听认证状态变更事件,等状态更新后再执行业务逻辑:

@inject AuthenticationStateProvider AuthStateProvider

@code {
    private ClaimsPrincipal _currentUser;

    protected override async Task OnInitializedAsync()
    {
        // 监听认证状态变更事件
        AuthStateProvider.AuthenticationStateChanged += async authStateTask =>
        {
            var authState = await authStateTask;
            _currentUser = authState.User;
            // 这里执行依赖认证状态的业务逻辑
            await LoadUserRelatedData();
        };

        // 先获取初始的匿名状态
        var initialState = await AuthStateProvider.GetAuthenticationStateAsync();
        _currentUser = initialState.User;
    }

    private async Task LoadUserRelatedData()
    {
        if (_currentUser.Identity.IsAuthenticated)
        {
            // 你的业务逻辑代码
        }
    }
}

关键注意事项

  • 别在GetAuthenticationStateAsync里做异步IO:这个方法会被Blazor框架频繁调用,而且调用时机可能远早于JS环境初始化,强行调用LocalStorage必然出错。
  • 必须调用NotifyAuthenticationStateChanged:当你从LocalStorage拿到token并构造好用户身份后,一定要用这个方法通知Blazor更新所有依赖认证状态的组件。
  • 精准捕获异常:针对电路断开、JS未初始化的情况,捕获InvalidOperationException,避免不必要的错误日志,同时回退到匿名身份。

内容的提问来源于stack exchange,提问作者runemonster

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 22:25:38