Blazor Server带JWT令牌无法访问[Authorize]页面的问题排查
我正在开发拆分API与Blazor Server的项目:API生成JWT令牌返回前端,前端将令牌存入HttpOnly Cookie后,访问带有@attribute [Authorize]的Dashboard页面时返回401未授权。但用该令牌在Scalar API测试时可正常访问API。已在Blazor的Program.cs配置JWT认证与授权,实现AuthHandler用于请求附加令牌,API端也配置了对应JWT验证参数,需排查无法访问授权页面的原因。
相关代码
登录时设置Cookie的代码
token = "eyJhbGciOiJIUz...."; var options = new CookieOptions { HttpOnly = true, Secure = false, SameSite = SameSiteMode.Strict }; httpContext.Response.Cookies.Append(Extension.AuthorizationName, token, options);
Blazor - Program.cs
builder.Services.AddHttpClient(Extension.AuthClient, client => { client.BaseAddress = new Uri(APIHelper.BackendUrl); }).AddHttpMessageHandler<AuthHandler>(); // JWT token 配置 var Audience = builder.Configuration["JwtSettings:Audience"]; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = APIHelper.BackendUrl; options.Audience = Audience; options.RequireHttpsMetadata = APIHelper.RequireHttpsMetadata; }); builder.Services.AddAuthorization();
AuthHandler.cs
private readonly NavigationManager _navigationManager; private readonly IHttpContextAccessor httpContextAccessor; public AuthHandler(NavigationManager navigationManager, IHttpContextAccessor _httpContextAccessor) { _navigationManager = navigationManager; httpContextAccessor = _httpContextAccessor; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var token = httpContextAccessor.HttpContext.Request.Headers[Extension.AuthorizationName]; if (!string.IsNullOrEmpty(token)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } var response = await base.SendAsync(request, cancellationToken); // 令牌过期(401)时跳转至登录页 if (response.StatusCode == HttpStatusCode.Unauthorized) { _navigationManager.NavigateTo("/login", true); // 重定向到登录页 } return response; }
API - Program.cs
var Issuer = builder.Configuration["JwtSettings:Issuer"]; var Audience = builder.Configuration["JwtSettings:Audience"]; var Token = builder.Configuration["JwtSettings:Token"]; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = Issuer, ValidateAudience = true, ValidAudience = Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Token)), ValidateIssuerSigningKey = true }; }); builder.Services.AddAuthorization();
排查方向
1. 页面授权的认证逻辑不匹配
Blazor Server页面的[Authorize]属性依赖服务器端的认证状态,当前配置的JwtBearer认证默认从Authorization头读取令牌,但页面初始请求(含SignalR连接)不会自动将Cookie中的令牌转为Authorization头,也不会主动验证Cookie内的JWT生成用户身份凭证,导致服务器端识别不到已认证用户。
2. AuthHandler仅作用于API请求,不影响页面授权
你实现的AuthHandler仅为HttpClient添加,只在Blazor调用API时生效,和页面本身的[Authorize]检查无关。页面授权失败是因为服务器端未获取到有效认证信息,与API调用的Handler逻辑不相关。
3. JWT验证参数不一致
Blazor端使用options.Authority配置,会通过OpenID Connect方式从授权服务器获取元数据验证令牌;但API端是手动配置TokenValidationParameters。需确保:
- Blazor端与API端的
Audience、Issuer完全一致 - 若未使用IdentityServer等授权服务器,Blazor端应放弃
Authority配置,改为和API端一致的手动TokenValidationParameters,否则会因验证逻辑不匹配导致认证失败。
4. Cookie传递限制
SameSiteMode.Strict会限制Cookie仅在同站点请求中发送,若Blazor Server与API域名不同,或SignalR连接未携带Cookie,会导致服务器端无法获取令牌,可尝试改为SameSiteMode.Lax测试Secure = false在HTTPS环境下,浏览器可能拒绝发送非Secure Cookie,需确保开发/生产环境配置匹配
5. 服务器端未从Cookie读取令牌
需修改Blazor端的JWT认证配置,添加从Cookie提取令牌的逻辑,让服务器端能读取并验证Cookie内的JWT:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Audience = Audience; options.RequireHttpsMetadata = APIHelper.RequireHttpsMetadata; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = builder.Configuration["JwtSettings:Issuer"], ValidateAudience = true, ValidAudience = Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Token"])), ValidateIssuerSigningKey = true }; // 从Cookie读取JWT令牌 options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Cookies[Extension.AuthorizationName]; if (!string.IsNullOrEmpty(token)) { context.Token = token; } return Task.CompletedTask; } }; });
同时确保Program.cs中中间件顺序正确:app.UseAuthentication();和app.UseAuthorization();需放在app.UseRouting();之后、app.MapBlazorHub();之前。
内容的提问来源于stack exchange,提问作者Jesper

