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

JWT授权失败排查:Identity Server访问[Authorize]端点返回Unauthorized

问题:生成JWT令牌后访问带[Authorize]的端点返回Unauthorized

我正在开发一个入门级Identity Server,目标是让服务生成JWT令牌,用于用户认证并配置授权策略。目前已成功生成令牌,但访问带有[Authorize]特性的端点时,持续收到Unauthorized响应。

免责声明:
以下的密钥和令牌均为模拟生成。请勿分享(更不要发布到网络论坛)任何密钥、JWT或敏感数据。我这么做是因为该应用不会投入生产,仅用于学习目的。


Program.cs

...
// Authorization
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
 {
     options.RequireHttpsMetadata = false;
     options.TokenValidationParameters = new TokenValidationParameters
     {
         IssuerSigningKey = new SymmetricSecurityKey(
             Encoding.ASCII.GetBytes("26837a75-3199-4e7d-8c31-2c3f53f42e83")),
         ValidateIssuer = false,
         ValidateAudience = false,
         ValidateLifetime = false,
         ValidateIssuerSigningKey = false,
         ValidateActor = false,
         ValidateSignatureLast = false,
         ValidateTokenReplay = false,
         ValidateWithLKG = false
     };
 });

// HTTP request pipeline
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseAuthentication();
app.MapControllers();
app.MapHealthChecks("/healthz");
app.Run();

令牌生成工厂(省略完整控制器,该代码已能生成Bearer令牌)

public string CreateToken(
        MetaToken metaToken)
    {
        var tokenDuration = GetTokenDuration(metaToken);
        var expiresAt = DateTime.UtcNow.Add(tokenDuration);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Expires = expiresAt,
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Exp, expiresAt.Ticks.ToString()),
                new Claim(JwtRegisteredClaimNames.Nbf, DateTimeOffset.UtcNow.ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, metaToken.CreatedAt.Ticks.ToString()),
                new Claim(JwtRegisteredClaimNames.Jti, metaToken.Id.ToString())
            })
        };
        return CreateTokenInternal(tokenDescriptor);
    }

    private string CreateTokenInternal(SecurityTokenDescriptor tokenDescriptor)
    {

        // Setup signing credentials
        var secretBytes = Encoding.ASCII.GetBytes("26837a75-3199-4e7d-8c31-2c3f53f42e83");
        var key = new SymmetricSecurityKey(secretBytes);
        var algorithm = SecurityAlgorithms.HmacSha256Signature;
        tokenDescriptor.SigningCredentials = new SigningCredentials(key, algorithm);

        // Create the token
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);

    }

问题排查与修复

1. 中间件顺序错误

HTTP请求管道中,认证逻辑必须在授权逻辑之前执行。当前代码里UseAuthorization()先于UseAuthentication()运行,导致授权校验在用户身份完成认证前就触发,直接返回Unauthorized。

修复后的管道代码:

app.UseHttpsRedirection();
app.UseAuthentication(); // 先完成用户身份认证
app.UseAuthorization();  // 再执行授权策略校验
app.MapControllers();
app.MapHealthChecks("/healthz");
app.Run();

2. JWT标准声明格式错误

exp、nbf、iat这些JWT标准声明要求使用Unix时间戳(秒),但当前代码用了Ticks或DateTimeOffset字符串,即使关闭了ValidateLifetime,错误的声明格式仍会让认证逻辑无法识别令牌合法性。

修复后的令牌生成代码:

public string CreateToken(MetaToken metaToken)
{
    var tokenDuration = GetTokenDuration(metaToken);
    var expiresAt = DateTime.UtcNow.Add(tokenDuration);
    // 转换为符合要求的Unix时间戳(秒)
    var unixExpiresAt = new DateTimeOffset(expiresAt).ToUnixTimeSeconds();
    var unixCurrentTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
    
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Expires = expiresAt,
        Subject = new ClaimsIdentity(new Claim[]
        {
            new Claim(JwtRegisteredClaimNames.Exp, unixExpiresAt.ToString()),
            new Claim(JwtRegisteredClaimNames.Nbf, unixCurrentTime.ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, unixCurrentTime.ToString()),
            new Claim(JwtRegisteredClaimNames.Jti, metaToken.Id.ToString())
        })
    };
    return CreateTokenInternal(tokenDescriptor);
}

3. 关闭签名验证存在风险(建议开启)

当前配置中ValidateIssuerSigningKey = false,会跳过令牌签名的合法性校验,即使是学习环境,也建议开启该选项,确保令牌确实由你的服务生成:

修复后的TokenValidationParameters:

options.TokenValidationParameters = new TokenValidationParameters
{
    IssuerSigningKey = new SymmetricSecurityKey(
        Encoding.ASCII.GetBytes("26837a75-3199-4e7d-8c31-2c3f53f42e83")),
    ValidateIssuer = false,
    ValidateAudience = false,
    ValidateLifetime = false, // 学习阶段可暂时关闭,后续建议开启
    ValidateIssuerSigningKey = true, // 开启签名验证
    ValidateActor = false,
    ValidateSignatureLast = false,
    ValidateTokenReplay = false,
    ValidateWithLKG = false
};

额外验证步骤

  • 生成令牌后,解析令牌检查声明格式是否正确、签名是否有效。
  • 确保请求时在Authorization请求头中正确携带Bearer {你的令牌}格式的内容。

内容的提问来源于stack exchange,提问作者sebsmgzz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 06:37:39