如何调试.NET 8中AWS Cognito JWT Bearer认证未通过问题
我正在将一个运行了9年、从未改动过的.NET Core 1.1 MVC Web服务升级到.NET 8.0,目前需要让服务验证AWS Cognito的Bearer令牌。我已经正确配置了Issuer(确认其字符串与解码JWT中的iss字段完全匹配),但调试输出仅显示:
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Debug: AuthenticationScheme: Bearer was not authenticated.
我已经将默认日志级别设置为Trace,并且单独指定了各组件的Trace级别:
{ "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Trace", "System": "Trace", "Microsoft": "Trace", "Microsoft.AspNetCore": "Trace", "Microsoft.AspNetCore.Authentication": "Trace" } } }
同时我设置了多项JwtBearer选项以增加日志信息:
options.TokenValidationParameters = new TokenValidationParameters { RequireSignedTokens = true, RequireExpirationTime = true, ClockSkew = new TimeSpan(0, 15, 0), RequireAudience = false, ValidateAudience = false, ValidateLifetime = true, ValidateIssuer = true, ValidIssuer = "proper aws url", LogValidationExceptions = true, LogTokenId = true, }; options.UseSecurityTokenValidators = true; options.IncludeErrorDetails = true;
但生成的跟踪日志仍极为有限(仅一行)且不明确。我肯定遗漏了某些配置,请问如何调试找出验证失败的原因?是否可以在认证链中插入自定义类,以查看哪个函数失败、传入的参数等信息?
1. 补全签名验证配置(最可能的遗漏点)
你当前只配置了Issuer验证,但JWT签名验证是核心环节——AWS Cognito的令牌需要用对应公钥验证签名。.NET 8的JwtBearer不会自动获取Cognito的公钥,必须手动配置:
options.TokenValidationParameters.IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) => { // 替换为你的Cognito区域和用户池ID var jwksUrl = $"https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json"; using var httpClient = new HttpClient(); var jwks = httpClient.GetStringAsync(jwksUrl).Result; var keys = new JsonWebKeySet(jwks).GetSigningKeys(); return keys.Where(k => k.Kid == kid); };
如果只是临时测试,可以设置RequireSignedTokens = false跳过签名验证,但生产环境必须开启该验证。
2. 手动捕获验证异常
即使开启了LogValidationExceptions,有时日志框架可能无法正确输出异常详情。可以通过自定义JwtBearerEvents直接捕获并打印:
options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { Console.WriteLine($"认证失败: {context.Exception.Message}"); if (context.Exception.InnerException != null) { Console.WriteLine($"内部异常: {context.Exception.InnerException.Message}"); } return Task.CompletedTask; }, OnTokenValidated = context => { Console.WriteLine("令牌验证成功"); return Task.CompletedTask; } };
这个事件会在验证失败时触发,直接返回具体错误原因(比如签名不匹配、令牌过期、Issuer不一致等)。
3. 插入自定义令牌验证器
如果需要更深入的调试,可以实现ISecurityTokenValidator来监控验证全流程:
public class DebugTokenValidator : ISecurityTokenValidator { private readonly JwtSecurityTokenHandler _innerValidator = new JwtSecurityTokenHandler(); public bool CanValidateToken => true; public int MaximumTokenSizeInBytes => _innerValidator.MaximumTokenSizeInBytes; public bool CanReadToken(string securityToken) { Console.WriteLine($"收到令牌: {securityToken}"); return _innerValidator.CanReadToken(securityToken); } public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) { try { Console.WriteLine("开始令牌验证..."); Console.WriteLine($"配置的ValidIssuer: {validationParameters.ValidIssuer}"); var principal = _innerValidator.ValidateToken(securityToken, validationParameters, out validatedToken); Console.WriteLine("验证成功"); return principal; } catch (Exception ex) { Console.WriteLine($"验证失败: {ex.Message}"); throw; } } }
然后替换默认验证器:
options.SecurityTokenValidators.Clear(); options.SecurityTokenValidators.Add(new DebugTokenValidator());
该自定义验证器会在验证前后输出关键参数,帮你定位具体出错步骤。
4. 检查令牌本身的有效性
先用JWT解码工具(比如本地jwt命令行工具)解码令牌,确认:
iss字段和配置的ValidIssuer完全一致(注意大小写、末尾是否带斜杠)exp字段是否在有效期内kid字段存在且能在Cognito的JWKS端点找到对应公钥
5. 确认中间件注册顺序
在.NET 8中,认证中间件的顺序至关重要,确保UseAuthentication()在UseAuthorization()之前,且在MapControllers()之前:
var builder = WebApplication.CreateBuilder(args); // 注册服务 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { /* 你的配置 */ }); builder.Services.AddAuthorization(); var app = builder.Build(); // 中间件顺序 app.UseHttpsRedirection(); app.UseAuthentication(); // 必须在UseAuthorization之前 app.UseAuthorization(); app.MapControllers(); app.Run();
内容的提问来源于stack exchange,提问作者Jeff

