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

Blazor WASM全局交互模式下SSR的JWT认证首屏渲染问题

在Blazor全局自动交互模式下实现SSR时的JWT授权渲染

核心思路

你的方向是对的——通过Cookie传递JWT让服务器在SSR时完成认证,同时用LocalStorage维护客户端API请求的令牌。关键是让服务器同时支持从Authorization头和Cookie读取JWT,并且登录时同步设置两者,这样既不用刷新页面,也能保证SSR渲染正确的授权内容。

具体实现步骤

1. 服务器端配置(支持Cookie读取JWT)

在Program.cs中配置JWT认证时,添加Cookie作为令牌来源:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };

        // 添加从Cookie读取JWT的逻辑
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                // 优先从Authorization头取,没有的话从Cookie取
                var token = context.Request.Cookies["AuthToken"];
                if (!string.IsNullOrEmpty(token))
                {
                    context.Token = token;
                }
                return Task.CompletedTask;
            }
        };
    });

// 启用认证和授权中间件
app.UseAuthentication();
app.UseAuthorization();

2. 登录接口同步设置Cookie和返回JWT

登录接口验证用户名密码后,生成JWT,同时通过响应头设置HttpOnly Cookie:

[HttpPost("login")]
public IActionResult Login([FromBody] LoginModel model)
{
    // 验证用户逻辑(示例)
    var user = ValidateUser(model.Username, model.Password);
    if (user == null) return Unauthorized();

    // 生成JWT令牌
    var token = GenerateJwtToken(user);

    // 设置HttpOnly Cookie,保障安全
    Response.Cookies.Append("AuthToken", token, new CookieOptions
    {
        HttpOnly = true, // 防止XSS窃取令牌
        Secure = builder.Environment.IsProduction(), // 生产环境强制HTTPS
        SameSite = SameSiteMode.Strict,
        Expires = DateTime.UtcNow.AddHours(2) // 和JWT过期时间保持一致
    });

    // 返回JWT给客户端存LocalStorage
    return Ok(new { Token = token });
}

3. 客户端处理逻辑

登录成功后,把JWT存到LocalStorage,同时配置HttpClient自动携带令牌:

@inject HttpClient Http
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager

<button @onclick="Login">登录</button>

@code {
    private async Task Login()
    {
        var loginModel = new { Username = "test", Password = "password" };
        var response = await Http.PostAsJsonAsync("/api/auth/login", loginModel);
        
        if (response.IsSuccessStatusCode)
        {
            var result = await response.ReadFromJsonAsync<AuthResult>();
            // 存令牌到LocalStorage
            await JSRuntime.InvokeVoidAsync("localStorage.setItem", "AuthToken", result.Token);
            
            // 给当前HttpClient添加Authorization头
            Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.Token);
            
            // 跳转至授权页面,无需刷新
            NavigationManager.NavigateTo("/dashboard");
        }
    }

    public class AuthResult { public string Token { get; set; } }
}

4. 全局HttpClient配置(可选)

封装一个带自动令牌注入的HttpClient服务,避免重复逻辑:

public class AuthHttpClientHandler : DelegatingHandler
{
    private readonly IJSRuntime _jsRuntime;

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

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "AuthToken");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

// 在Program.cs注册服务
builder.Services.AddScoped<AuthHttpClientHandler>();
builder.Services.AddHttpClient("AuthClient")
    .AddHttpMessageHandler<AuthHttpClientHandler>();

方案优势

  • 无需页面刷新:登录后服务器通过响应头设置Cookie,客户端同步存LocalStorage,后续交互用LocalStorage令牌,SSR请求自动带Cookie完成认证。
  • 无UI闪烁:首次SSR时服务器已完成认证,直接渲染对应授权状态的UI,不需要客户端后续补全占位符。
  • 安全可靠:HttpOnly Cookie防范XSS攻击,LocalStorage令牌仅用于客户端API请求,各司其职。

注意事项

  • 确保JWT过期时间与Cookie过期时间一致,避免令牌失效后Cookie残留导致认证异常。
  • 生产环境必须启用HTTPS,配合Secure Cookie属性防止令牌被明文窃取。
  • 令牌刷新时,需同时更新LocalStorage和Cookie(可通过API返回新令牌,服务器同步设置Cookie)。

内容的提问来源于stack exchange,提问作者Michał Woźniak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:32:41