.NET Core 6 Web API部署IIS后间歇性IDX20803异常求助
问题背景
已将.NET Core 6 Web API部署至IIS,应用池标识配置为管理员账户,但调用API时间歇性出现IO异常,相关日志如下:
Exception occurred while processing message.
System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'System.String'.
at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Exception occurred while processing message. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'System.String'. ---> System.IO.IOException: IDX20807: Unable to retrieve document from: 'System.String'. HttpResponseMessage: 'System.Net.Http.HttpResponseMessage', HttpResponseMessage.Content: 'System.String'. at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel)
--- End of inner exception stack trace ---
at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
当前Startup.cs中的JWT配置:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = false, ValidIssuer = Configuration["Authorization:Jwt:Issuer"], ValidAudience = Configuration["Authorization:Jwt:Audience"], }; options.MetadataAddress = Configuration["Authorization:Jwt:MetaAddress"]; options.RequireHttpsMetadata = false; options.BackchannelHttpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = delegate { return true; } }; });
已尝试添加options.Authority = Configuration["Authorization:Jwt:Issuer"];并将IIS应用池的LoadUserProfile设置为true,但问题仍未解决。
问题原因分析
日志中的IDX20803和IDX20807错误表明,JWT认证中间件间歇性无法从配置的元数据地址获取OpenID配置文档,常见原因包括:
- 元数据地址配置无效或未正确读取,导致请求地址为占位符
System.String - Backchannel HttpClient的超时、连接数配置不合理,引发间歇性请求失败
MetadataAddress与Authority配置冲突,导致框架无法正确定位配置文档- IIS应用池的网络访问受限,无法稳定连接到元数据地址
- 默认配置刷新机制的重试策略不足,导致刷新失败时无回退逻辑
解决方法
1. 验证元数据地址的有效性
- 确认
Authorization:Jwt:MetaAddress在配置文件(如appsettings.json)中是完整且可访问的OpenID配置地址,格式通常为http://your-identity-server/.well-known/openid-configuration - 在部署API的服务器上,直接用浏览器或curl命令访问该地址,确认能正常返回JSON格式的配置内容
- 检查配置项是否被正确读取,可通过打印日志或调试确认
options.MetadataAddress的实际值
2. 优化Backchannel HttpClient配置
默认的Backchannel HttpClient参数可能无法应对高并发或慢响应的场景,可自定义配置:
var backchannelHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = delegate { return true; }, MaxConnectionsPerServer = 100, // 提升并发连接数 UseCookies = false, AllowAutoRedirect = true }; var backchannelHttpClient = new HttpClient(backchannelHandler) { Timeout = TimeSpan.FromSeconds(30), // 延长超时时间,避免短时间内请求失败 DefaultRequestHeaders = { CacheControl = new CacheControlHeaderValue { NoCache = true } } }; options.Backchannel = backchannelHttpClient;
3. 解决JWT配置冲突
同时设置MetadataAddress和Authority会导致框架逻辑冲突,二选一即可:
- 若使用
Authority:确保其值为身份服务器的根地址,框架会自动推导元数据地址({Authority}/.well-known/openid-configuration),此时需移除MetadataAddress配置 - 若使用
MetadataAddress:移除Authority配置,避免自动推导逻辑干扰
4. 检查IIS应用池的网络权限
- 确认服务器防火墙允许出站访问元数据地址的对应端口(如80/443)
- 若身份服务器在内部网络,检查DNS解析是否正常,或直接使用IP地址测试
- 若服务器处于代理环境,需为Backchannel HttpClient配置代理:
backchannelHandler.Proxy = new WebProxy("http://your-proxy-address:proxy-port"); backchannelHandler.UseProxy = true;
5. 自定义配置刷新策略
调整ConfigurationManager的刷新间隔和重试逻辑,减少间歇性失败:
using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; // ... var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>( options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), new HttpDocumentRetriever(options.Backchannel) ) { AutomaticRefreshInterval = TimeSpan.FromHours(2), // 延长自动刷新间隔 RefreshInterval = TimeSpan.FromMinutes(5), // 设置失败后的重试间隔 DefaultRefreshInterval = TimeSpan.FromMinutes(5) }; options.ConfigurationManager = configurationManager;
内容的提问来源于stack exchange,提问作者Leethiyal

