容器化.NET 8 ASP.NET API的JWT验证失败与网络请求问题
我正在将.NET 8版本的ASP.NET Web API进行容器化处理。该API在Visual Studio本地运行及Azure部署时均正常,使用从Entra获取的JWT调用接口毫无问题,授权配置如下:
// Add authentication services builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { builder.Configuration.Bind("AzureAd", options); options.Authority = $"{builder.Configuration["AzureAd:Instance"]}{builder.Configuration["AzureAd:TenantId"]}/v2.0"; // The valid audiences are both the Client ID(options.Audience) and api://{ClientID} options.TokenValidationParameters.ValidAudiences = [ builder.Configuration["AzureAd:ClientId"], $"api://{builder.Configuration["AzureAd:ClientId"]}", ]; // Valid issuers here: options.TokenValidationParameters.ValidIssuers = [ $"https://sts.windows.net/{builder.Configuration["AzureAd:TenantId"]}/", $"{builder.Configuration["AzureAd:Instance"]}{builder.Configuration["AzureAd:TenantId"]}/", $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/v2.0" ]; });
但容器化后,无需授权的接口可正常调用,调用带有[Authorize]属性的控制器方法时却返回401 Unauthorized,响应头信息如下:
content-length: 0 date: Tue,16 Apr 2024 19:17:36 GMT server: Kestrel www-authenticate: Bearer error="invalid_token",error_description="The signature key was not found"
相同代码在本地运行正常,容器中则无法工作。推测是JWT验证代码尝试从Authority获取签名密钥时失败,本地环境允许该出站请求,但容器环境未允许。
为验证该假设,在控制器中添加测试方法:
var client = new HttpClient(); client.BaseAddress = new Uri("https://www.microsoft.com/"); var resp = await client.GetAsync(string.Empty); html = await resp.Content.ReadAsStringAsync();
测试容器是否能发起任何出站请求,结果失败,错误信息如下:
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
不想在容器中手动安装证书或密钥(担心证书变更),请问是否有办法解决容器内的出站HTTP请求问题?
以下几种方法可解决容器内SSL证书信任问题,无需手动安装证书:
1. 切换到完整版本的官方.NET基础镜像
若当前使用mcr.microsoft.com/dotnet/aspnet:8.0-alpine这类轻量镜像,替换为非Alpine的完整镜像(如mcr.microsoft.com/dotnet/aspnet:8.0)。这类镜像预装了完整的根证书信任链,可自动信任主流CA颁发的证书,包括Microsoft服务的证书。
2. 临时禁用证书验证(仅测试环境)
如果是测试环境排查问题,可在JWTBearer配置中跳过证书验证,但生产环境严禁使用:
.AddJwtBearer(options => { // 保留原有配置... options.BackchannelHttpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator }; })
3. 挂载主机证书目录到容器
运行容器时,将主机的证书目录挂载到容器对应路径,让容器直接使用主机的信任证书:
- Windows主机:
docker run -v "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\Certificates:/root/.aspnet/https" your-image-name - Linux主机:
docker run -v "/etc/ssl/certs:/etc/ssl/certs:ro" your-image-name
4. 配置HttpClient使用系统默认证书验证
对于.NET 8,默认HttpClient会尝试读取系统证书存储。如果使用自定义HttpClient,确保未覆盖证书验证逻辑:
builder.Services.AddHttpClient("MicrosoftServices", client => { client.BaseAddress = new Uri("https://login.microsoftonline.com/"); }).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { UseDefaultCredentials = true, ServerCertificateCustomValidationCallback = HttpClientHandler.DefaultServerCertificateCustomValidationCallback });
内容的提问来源于stack exchange,提问作者Allan Alderman

