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

Blazor .NET 8自定义认证状态提供者使用SessionStorageService报错求助

解决Blazor Server(.NET 8)中CustomAuthenticationStateProvider使用SessionStorage的预渲染错误

问题原因

.NET 8的Blazor Server默认启用预渲染机制,在预渲染阶段代码在服务器端执行,无法调用JavaScript互操作(比如访问浏览器的SessionStorage)。而AuthorizeView组件会在预渲染时调用GetAuthenticationStateAsync方法,此时CustomAuthenticationStateProvider尝试访问SessionStorage就会触发错误:

System.InvalidOperationException: 'JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.'

解决方案

方案1:禁用预渲染(快速解决)

直接关闭全局预渲染,让所有组件都在客户端执行,这样就能正常访问SessionStorage。修改Program.cs中的渲染模式配置:

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(prerender: false);

优点:实现简单,无需修改认证逻辑
缺点:首屏加载速度略有下降,失去预渲染带来的SEO优化


方案2:保留预渲染,适配认证逻辑

通过PersistentComponentState在预渲染阶段传递认证状态,同时在客户端初始化后重新从SessionStorage获取最新状态,兼顾预渲染和SessionStorage的使用。

步骤1:修改CustomAuthenticationStateProvider

注入PersistentComponentState,区分预渲染和客户端运行时的认证状态获取逻辑:

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ISessionStorageService _sessionStorageService;
    private readonly PersistentComponentState _persistentComponentState;
    private readonly ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());
    private readonly TaskCompletionSource<AuthenticationState> _initialStateTcs = new();

    public CustomAuthenticationStateProvider(ISessionStorageService sessionStorageService, PersistentComponentState persistentComponentState)
    {
        _sessionStorageService = sessionStorageService;
        _persistentComponentState = persistentComponentState;

        // 预渲染时从PersistentComponentState读取token
        if (_persistentComponentState.TryTakeFromJson<string>("authToken", out var token))
        {
            _initialStateTcs.SetResult(BuildAuthenticationState(token));
        }
        else
        {
            _initialStateTcs.SetResult(new AuthenticationState(_anonymous));
        }
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        // 预渲染阶段返回初始状态
        if (_persistentComponentState.IsPersisting)
        {
            return _initialStateTcs.Task;
        }

        // 客户端运行时从SessionStorage获取状态
        return GetAuthenticationStateFromSessionStorageAsync();
    }

    private async Task<AuthenticationState> GetAuthenticationStateFromSessionStorageAsync()
    {
        try
        {
            var token = await _sessionStorageService.GetItemAsync<string>("token");
            return BuildAuthenticationState(token);
        }
        catch
        {
            return new AuthenticationState(_anonymous);
        }
    }

    private AuthenticationState BuildAuthenticationState(string? token)
    {
        if (string.IsNullOrEmpty(token))
        {
            return new AuthenticationState(_anonymous);
        }

        var identity = new ClaimsIdentity(JwtParser.ParseClaimsFromJwt(token), "jwt");
        return new AuthenticationState(new ClaimsPrincipal(identity));
    }

    public void AuthenticateUser(string token)
    {
        var state = BuildAuthenticationState(token);
        // 将token存入PersistentComponentState,供预渲染使用
        _persistentComponentState.PersistAsJson("authToken", token);
        NotifyAuthenticationStateChanged(Task.FromResult(state));
    }
}

步骤2:确保登录时同步状态

登录成功调用AuthenticateUser方法时,token会同时存入SessionStorage和PersistentComponentState,保证预渲染阶段能获取到认证状态。

步骤3:客户端初始化后更新状态(可选)

如果需要确保客户端始终获取最新的SessionStorage状态,可以在MainLayout.razor中添加客户端初始化逻辑:

@inject AuthenticationStateProvider AuthStateProvider
@implements IAsyncDisposable

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 客户端渲染完成后,强制更新认证状态
            var customProvider = (CustomAuthenticationStateProvider)AuthStateProvider;
            var state = await customProvider.GetAuthenticationStateFromSessionStorageAsync();
            customProvider.NotifyAuthenticationStateChanged(Task.FromResult(state));
        }
    }

    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}

优点:保留预渲染的SEO和首屏加载优势
缺点:需要修改认证逻辑,实现相对复杂

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:44:53