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
相关产品推荐
相关产品推荐

