自托管KeyCloak与Ocelot网关集成时出现IDX10500签名验证失败错误
问题:KeyCloak + Ocelot API网关JWT签名验证失败(IDX10500)
在Docker部署的.NET Ocelot网关集成自托管KeyCloak时,能正常获取JWT令牌,但访问需要认证的/api/weatherforecast路由时,出现签名验证失败错误:IDX10500: Signature validation failed. No security keys were provided to validate the signature。容器间通信正常,本地浏览器可正常获取KeyCloak的OIDC配置。
相关配置
1. Program.cs 核心代码
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Ocelot.DependencyInjection; using Ocelot.Middleware; var builder = WebApplication.CreateBuilder(args); // 添加Ocelot配置 builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); // 注册Ocelot服务 builder.Services.AddOcelot(builder.Configuration); builder.Services.AddHttpClient(); // Keycloak认证配置 builder.Services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer("KeyCloak", options => { options.Authority = "http://localhost:5002/realms/test-realm"; options.Audience = "api-gw"; options.MetadataAddress = "http://keycloak:5002/realms/test-realm/.well-known/openid-configuration"; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "http://localhost:5002/realms/test-realm", ValidateAudience = true, ValidAudience = "api-gw", ValidateLifetime = true, ValidateIssuerSigningKey = true, ClockSkew = TimeSpan.Zero }; options.Events = new JwtBearerEvents { OnTokenValidated = context => { Console.WriteLine("Token Validated."); return Task.CompletedTask; }, OnAuthenticationFailed = context => { Console.WriteLine($"Token Validation Failed: {context.Exception.Message}"); return Task.CompletedTask; } }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.UseHttpsRedirection(); app.UseOcelot().Wait(); app.Run();
2. Ocelot.json 路由配置
{ "Routes": [ { "DownstreamPathTemplate": "/login", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "auth-service", "Port": 8080 } ], "UpstreamPathTemplate": "/login", "UpstreamHttpMethod": ["Post"] }, { "DownstreamPathTemplate": "/todos/{id}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "jsonplaceholder.typicode.com", "Port": 443 } ], "UpstreamPathTemplate": "/api/todos/{id}", "UpstreamHttpMethod": ["Get"] }, { "DownstreamPathTemplate": "/weatherforecast", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "temp-service", "Port": 8080 } ], "UpstreamPathTemplate": "/api/weatherforecast", "UpstreamHttpMethod": ["Get"], "AuthenticationOptions": { "AuthenticationProviderKey": "KeyCloak", "AllowedScopes": [] } } ], "GlobalConfiguration": { "BaseUrl": "https://localhost:5000" } }
3. 错误日志
info: Ocelot.Authentication.Middleware.AuthenticationMiddleware[0] 2024-12-16 20:44:49 requestId: 0HN8U187GSHNG:00000003, previousRequestId: No PreviousRequestId, message: 'The path '/api/weatherforecast' is an authenticated route! AuthenticationMiddleware checking if client is authenticated...' 2024-12-16 20:44:50 Token Validation Failed: IDX10500: Signature validation failed. No security keys were provided to validate the signature. 2024-12-16 20:44:50 warn: Ocelot.Authentication.Middleware.AuthenticationMiddleware[0] 2024-12-16 20:44:50 requestId: 0HN8U187GSHNG:00000003, previousRequestId: No PreviousRequestId, message: 'Client has NOT been authenticated for path '/api/weatherforecast' and pipeline error set. Request for authenticated route '/api/weatherforecast' was unauthenticated;' 2024-12-16 20:44:50 warn: Ocelot.Responder.Middleware.ResponderMiddleware[0] 2024-12-16 20:44:50 requestId: 0HN8U187GSHNG:00000003, previousRequestId: No PreviousRequestId, message: 'Error Code: UnauthenticatedError Message: Request for authenticated route '/api/weatherforecast' was unauthenticated errors found in ResponderMiddleware. Setting error response for request path:/api/weatherforecast, request method: GET'
解决方案
1. 避免手动覆盖TokenValidationParameters实例
当你手动创建new TokenValidationParameters()时,会完全替换JwtBearer中间件从KeyCloak Metadata自动加载的配置(包括签名验证密钥),导致没有可用密钥验证签名。正确的做法是修改现有参数,而非新建实例:
.AddJwtBearer("KeyCloak", options => { options.Authority = "http://keycloak:5002/realms/test-realm"; options.Audience = "api-gw"; options.RequireHttpsMetadata = false; // 仅修改需要调整的参数,保留中间件自动加载的密钥等配置 options.TokenValidationParameters.ClockSkew = TimeSpan.Zero; options.TokenValidationParameters.ValidAudience = "api-gw"; // 事件代码保持不变 options.Events = new JwtBearerEvents{...}; });
2. 统一容器内部的KeyCloak访问地址
网关运行在Docker容器中,需使用容器网络内的KeyCloak地址(容器名keycloak)而非宿主机localhost,否则会出现Issuer不匹配、密钥加载失败的问题:
- 将
options.Authority和ValidIssuer统一改为http://keycloak:5002/realms/test-realm - 无需手动设置
MetadataAddress,中间件会自动通过Authority推导OIDC配置地址
3. 确保认证Scheme匹配
将默认认证Scheme设置为KeyCloak,与Ocelot路由配置中的AuthenticationProviderKey一致:
builder.Services .AddAuthentication("KeyCloak") // 设置默认Scheme为KeyCloak .AddJwtBearer("KeyCloak", options => {...});
内容的提问来源于stack exchange,提问作者Corgam
相关产品推荐
相关产品推荐

