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

Blazor Server Side应用如何存储JWT令牌以访问独立API服务器?

Blazor Server Side 存储JWT令牌的可行方案

核心思路:利用Circuit作用域的服务器端存储

Blazor Server每个用户会话对应一个独立的Circuit,服务器端的Scoped服务会绑定到这个Circuit,是最可靠的会话级存储方式。

1. 创建令牌存储服务

public interface ITokenStorage
{
    string? Token { get; set; }
    Task<string?> GetTokenAsync();
    Task SetTokenAsync(string? token);
}

public class TokenStorage : ITokenStorage
{
    private string? _token;

    public string? Token 
    { 
        get => _token; 
        set => _token = value; 
    }

    public Task<string?> GetTokenAsync()
    {
        return Task.FromResult(_token);
    }

    public Task SetTokenAsync(string? token)
    {
        _token = token;
        return Task.CompletedTask;
    }
}

2. 注册Scoped服务

在Program.cs中添加:

builder.Services.AddScoped<ITokenStorage, TokenStorage>();

3. 登录时存储令牌

在登录组件中注入ITokenStorage,获取JWT后存入:

@inject ITokenStorage TokenStorage
@inject HttpClient HttpClient
@inject NavigationManager NavigationManager

// 登录逻辑示例
private async Task HandleLogin()
{
    var loginRequest = new { Username = Username, Password = Password };
    var response = await HttpClient.PostAsJsonAsync("https://your-api-server/auth/login", loginRequest);
    
    if (response.IsSuccessStatusCode)
    {
        var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
        await TokenStorage.SetTokenAsync(tokenResponse.AccessToken);
        NavigationManager.NavigateTo("/");
    }
}

4. 调用API时自动携带令牌

封装一个API请求服务,自动从ITokenStorage获取令牌并添加到请求头:

public class ApiService
{
    private readonly HttpClient _httpClient;
    private readonly ITokenStorage _tokenStorage;

    public ApiService(HttpClient httpClient, ITokenStorage tokenStorage)
    {
        _httpClient = httpClient;
        _tokenStorage = tokenStorage;
    }

    public async Task<T?> GetAsync<T>(string url)
    {
        var token = await _tokenStorage.GetTokenAsync();
        if (!string.IsNullOrEmpty(token))
        {
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }

        var response = await _httpClient.GetFromJsonAsync<T>(url);
        return response;
    }

    // 同理封装Post、Put、Delete等方法
}

注册这个服务:

builder.Services.AddScoped<ApiService>();

兼顾后续JS框架迁移的方案:结合LocalStorage

如果要为一年后切换到JS框架做准备,可以将令牌同时存在服务器端Scoped存储和前端LocalStorage:

1. 添加LocalStorage操作服务

public interface ILocalStorageService
{
    Task<string?> GetItemAsync(string key);
    Task SetItemAsync(string key, string value);
    Task RemoveItemAsync(string key);
}

public class LocalStorageService : ILocalStorageService
{
    private readonly IJSRuntime _jsRuntime;

    public LocalStorageService(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public async Task<string?> GetItemAsync(string key)
    {
        return await _jsRuntime.InvokeAsync<string?>("localStorage.getItem", key);
    }

    public async Task SetItemAsync(string key, string value)
    {
        await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, value);
    }

    public async Task RemoveItemAsync(string key)
    {
        await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
    }
}

注册为Scoped服务:

builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();

2. 登录时同步存储

// 登录成功后
await TokenStorage.SetTokenAsync(tokenResponse.AccessToken);
await LocalStorageService.SetItemAsync("authToken", tokenResponse.AccessToken);

3. 页面初始化时恢复令牌

在根组件(如MainLayout.razor)的OnInitializedAsync中恢复令牌:

@inject ITokenStorage TokenStorage
@inject ILocalStorageService LocalStorageService

protected override async Task OnInitializedAsync()
{
    var token = await LocalStorageService.GetItemAsync("authToken");
    if (!string.IsNullOrEmpty(token) && string.IsNullOrEmpty(await TokenStorage.GetTokenAsync()))
    {
        await TokenStorage.SetTokenAsync(token);
    }
}

这样用户刷新页面后,服务器端Circuit重建时能从LocalStorage恢复令牌,后续切换JS框架时也能直接读取LocalStorage中的令牌。


为什么之前的方案无效?

  • Cookie:Blazor Server中直接操作Cookie需在初始渲染/重定向阶段,SignalR连接建立后修改Cookie不会即时生效;若用HttpOnly Cookie,服务器端需通过IHttpContextAccessor读取,但要注意Circuit初始化时获取,后续HttpContext可能不可用。
  • LocalStorage:之前可能只存了前端,没同步到服务器端Scoped存储,导致服务器端调用API时拿不到令牌。
  • 会话内存:若未正确注册为Scoped服务,可能导致令牌被多个会话共享,或Circuit重建后丢失。

内容的提问来源于stack exchange,提问作者Age of Empires

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:05:10