基于Ocelot的.NET微服务网关如何实现JWT授权与API Key转发
Ocelot网关统一JWT校验+自动API Key注入实现方案
你考虑的DelegatingHandler确实能实现需求,但不是最优选择——它作用于Ocelot内部转发的HTTP客户端实例,执行时机偏后,无法直接复用Ocelot内置的路由匹配、认证能力,后续维护授权规则的成本比较高。
更推荐用Ocelot原生管道扩展+路由元数据配置的方案,全程不需要编写任何网关侧的业务控制器,所有逻辑和路由配置绑定,对原有服务无侵入。
具体实现步骤
1. 网关注入JWT认证能力
直接复用.NET和Ocelot原生的JWT认证组件,不需要自己写令牌校验逻辑,校验参数和现有Authentication Provider服务保持一致即可:
// Program.cs 服务注册部分 var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication("GatewayJwt") .AddJwtBearer("GatewayJwt", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "你的认证服务标识", ValidateAudience = true, ValidAudience = "微服务集群标识", ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("JWT签名密钥")) }; }); builder.Services.AddAuthorization(); builder.Services.AddOcelot();
2. 给目标路由配置认证规则和元数据
不需要写额外控制器,直接在ocelot.json中给需要API Key转发的路由绑定认证方案、要求的Claims规则、对应下游服务的API Key即可,其他原有路由保持原有配置不受影响:
{ "Routes": [ { "DownstreamPathTemplate": "/新微服务接口路径/{everything}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [{"Host": "新微服务地址", "Port": 服务端口}], "UpstreamPathTemplate": "/api/new-service/{everything}", "UpstreamHttpMethod": ["Get", "Post", "Put", "Delete"], // 绑定该路由需要走的JWT认证方案 "AuthenticationOptions": { "AuthenticationProviderKey": "GatewayJwt", "AllowedScopes": [] }, // 自定义元数据:配置授权规则和下游API Key "Metadata": { "RequiredClaim": "permission:access_new_service", "TargetApiKey": "该微服务分配的有效API Key" } } // 其他原有路由不需要网关认证的话,不用加上述配置,保持原有逻辑即可 ] }
3. 编写轻量管道中间件完成校验和请求头注入
中间件嵌入Ocelot原生请求管道,在路由匹配、JWT校验完成后执行:判断当前路由是否配置了API Key转发规则,校验JWT中的Claims是否满足要求,校验通过就给请求头注入API Key,不通过直接返回403:
public class ApiKeyInjectMiddleware { private readonly RequestDelegate _next; public ApiKeyInjectMiddleware(RequestDelegate next) => _next = next; public async Task InvokeAsync(HttpContext context) { var currentRoute = context.Items.DownstreamRoute(); // 没有配置API Key元数据的路由直接跳过,走原有逻辑 if (currentRoute?.Metadata == null || !currentRoute.Metadata.ContainsKey("TargetApiKey") || !currentRoute.Metadata.ContainsKey("RequiredClaim")) { await _next(context); return; } // 校验Claims var requiredClaim = currentRoute.Metadata["RequiredClaim"].Split(':'); var claimType = requiredClaim[0]; var claimValue = requiredClaim.Length > 1 ? requiredClaim[1] : null; var claimValid = context.User.Claims.Any(c => c.Type == claimType && (claimValue == null || c.Value == claimValue) ); if (!claimValid) { context.Response.StatusCode = StatusCodes.Status403Forbidden; return; } // 注入API Key请求头,Ocelot转发时会自动携带 context.Request.Headers.TryAdd("X-Api-Key", currentRoute.Metadata["TargetApiKey"]); await _next(context); } } public static class ApiKeyInjectMiddlewareExtensions { public static IApplicationBuilder UseApiKeyInject(this IApplicationBuilder app) => app.UseMiddleware<ApiKeyInjectMiddleware>(); }
4. 按正确顺序注册中间件
注意中间件顺序,必须放在认证授权之后、Ocelot核心转发逻辑之前,才能拿到已经校验完成的用户身份信息:
// Program.cs 中间件管道配置 var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // 注册自定义API Key注入中间件 app.UseApiKeyInject(); // 接入Ocelot核心逻辑 await app.UseOcelot(); app.Run();
方案优势
- 零额外控制器代码:新增需要网关认证+API Key转发的微服务时,只需要在
ocelot.json中添加对应路由配置即可,不需要修改网关业务代码。 - 兼容现有逻辑:未配置元数据的原有路由完全走之前的下游独立认证流程,支持灰度收敛认证逻辑,不需要一次性全量改造。
- 灵活可扩展:如果后续需要支持复杂授权规则(多Claims校验、角色校验、动态拉取API Key),只需要修改中间件逻辑即可,所有路由统一生效。如果对安全性要求高,还可以去掉配置文件中的明文API Key,在中间件中根据路由信息从密钥管理服务动态拉取对应密钥。
内容的提问来源于stack exchange,提问作者Vlad Radu
相关产品推荐
相关产品推荐

