.NET 5/.NET Core 3.1 JWT认证GetConfigurationAsync任务取消异常求助
异常根因说明
你遇到的TaskCanceledException抛出在JWT认证中间件拉取Identity Server的OIDC元数据配置阶段,只有在应用首次启动、配置缓存过期需要刷新时才会触发远端请求,网络不稳定、Identity Server响应超时、请求被网关/防火墙拦截都会触发该异常,本地环境不会遇到是因为本地和Identity Server的网络连通性远优于生产环境。
排查思路
- 先在生产环境所在服务器上手动请求Identity Server的OIDC配置地址
{Authority地址}/.well-known/openid-configuration,多次测试确认平均响应时长、是否有丢包、超时的情况 - 开启JwtBearer中间件的Debug日志,记录每次拉取配置的耗时、返回状态,配置方式如下:
在appsettings.json中添加日志规则:
{ "Logging": { "LogLevel": { "Microsoft.AspNetCore.Authentication.JwtBearer": "Debug", "Microsoft.IdentityModel.Protocols": "Debug" } } }
- 检查生产环境的防火墙、WAF、网关规则,是否有对Identity Server的出站请求做频率限制、超时截断
解决方案
1. 调整JwtBearer中间件的 backchannel 超时和重试策略
默认的backchannel超时时间只有15秒,网络波动时很容易触发超时,同时可以配置重试机制提升请求成功率,修改你的JWT配置代码:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, config => { config.Authority = appSettings.IdentiyServerConnection.Authority; config.Audience = appSettings.IdentiyServerConnection.Audience; config.RequireHttpsMetadata = appSettings.IdentiyServerConnection.RequireHttpsMetadata; // 调整 backchannel 超时时间到60秒 config.BackchannelTimeout = TimeSpan.FromSeconds(60); // 配置带重试的HttpMessageHandler,最多重试3次,每次间隔指数退避 config.BackchannelHttpHandler = new Polly.Retry.RetryPolicyHandler<HttpResponseMessage>( Policy.Handle<HttpRequestException>() .Or<TaskCanceledException>() .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))) ) { InnerHandler = WebHostEnvironment.IsProduction() ? new HttpClientHandler() : new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator } }; if (WebHostEnvironment.IsDevelopment()) { config.TokenValidationParameters.ClockSkew = TimeSpan.FromDays(10); } });
如果项目没有引入Polly,也可以自己实现简单的重试逻辑,或者直接拉长超时时间即可
2. 延长OIDC配置的缓存时长
默认配置缓存时长是5分钟,你可以调整到更长的时间减少拉取频率,在配置中添加:
config.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>( $"{config.Authority.TrimEnd('/')}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), new HttpDocumentRetriever(config.Backchannel) { RequireHttps = config.RequireHttpsMetadata }) { // 缓存24小时 AutomaticRefreshInterval = TimeSpan.FromHours(24), // 配置过期后最多可以再用1小时,避免拉取失败直接报错 RefreshInterval = TimeSpan.FromHours(1) };
3. 配置全局异常捕获过滤该类非核心异常
如果该异常不会影响用户正常使用(失败后下一次请求会重新拉取,大部分时候第二次就能成功),可以在全局异常中间件中过滤该类异常,避免不必要的告警:
app.UseExceptionHandler(builder => { builder.Run(async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); if (exceptionHandlerPathFeature?.Error is TaskCanceledException tcEx && tcEx.StackTrace.Contains("Microsoft.IdentityModel.Protocols.ConfigurationManager")) { // 仅记录Warning级别的日志,不上报告警 Log.Warning(tcEx, "拉取OIDC配置超时,将自动重试"); context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; return; } // 其他异常正常处理 }); });
内容的提问来源于stack exchange,提问作者bfahm
相关产品推荐
相关产品推荐

