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

Blazor Server带JWT令牌无法访问[Authorize]页面的问题排查

问题排查:Blazor Server 授权页面返回401未授权

我正在开发拆分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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 02:52:33