如何使用Azure Authentication保护代码优先的gRPC端点?
解决方案:用Azure JWT保护protobuf-net.Grpc端点
一、服务器端配置排查与修正
1. 确保protobuf-net.Grpc服务注册
在Program.cs中添加代码优先gRPC服务的注册:
builder.Services.AddCodeFirstGrpc();
2. 修正中间件管道顺序
gRPC认证依赖正确的中间件顺序,必须保证UseAuthentication和UseAuthorization在UseRouting之后、UseEndpoints之前:
app.UseRouting(); // 认证授权中间件必须放在此处 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 保留原REST端点映射 endpoints.MapGrpcService<ServerGreeterService>(); // 映射你的gRPC服务 // 可选:添加反射服务方便客户端生成代码 endpoints.MapCodeFirstGrpcReflectionService(); });
3. 优化JWT令牌检索配置
显式配置JWTBearer的令牌检索逻辑,确保能从gRPC元数据(HTTP/2头)中正确读取令牌:
builder.Services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(options => { builder.Configuration.Bind("Authorization:AzureAd", options); options.UseSecurityTokenValidators = true; // 显式指定令牌检索规则,兼容gRPC元数据传递 options.TokenRetriever = request => { // 优先从Authorization头获取 var token = TokenRetrieval.FromAuthorizationHeader()(request); if (string.IsNullOrEmpty(token)) { // 可选:支持从查询参数获取(gRPC场景一般不需要) token = TokenRetrieval.FromQueryString()(request); } return token; }; }, options => { builder.Configuration.Bind("Authorization:AzureAd", options); });
4. 验证Azure AD配置匹配
检查服务器端appsettings.json中的Authorization:AzureAd配置,确保Audience与客户端获取的JWT令牌的aud声明完全一致:
"Authorization": { "AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "你的租户ID", "ClientId": "你的服务器应用ID", "Audience": "api://你的服务器应用ID" // 必须与JWT的aud声明匹配 } }
可以用jwt.io解析客户端获取的令牌,确认aud、iss等声明符合服务器配置要求。
二、客户端配置修正
1. 注册HttpContextAccessor
客户端代码依赖IHttpContextAccessor获取当前请求的令牌,需在客户端Program.cs中添加注册:
builder.Services.AddHttpContextAccessor();
2. 验证令牌传递逻辑
确保客户端传递的Authorization头格式正确,避免重复添加Bearer前缀:
.AddCallCredentials((context, metadata, serviceProvider) => { var httpContext = serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext; if (httpContext != null && httpContext.Request.Headers.TryGetValue("Authorization", out var authHeader)) { // 直接使用原始头值,避免拆分后重复添加Bearer metadata.Add("Authorization", authHeader.ToString()); } return Task.CompletedTask; });
三、调试排查方法
1. 服务器端查看请求头
添加中间件打印接收到的元数据,确认Authorization头是否正确传递:
// 放在UseAuthentication之前 app.Use(async (context, next) => { if (context.Request.Headers.ContainsKey("Authorization")) { Console.WriteLine($"Received Authorization: {context.Request.Headers["Authorization"]}"); } await next(); });
2. 客户端验证元数据发送
在客户端AddCallCredentials中添加日志,确认令牌已正确添加到元数据:
.AddCallCredentials((context, metadata, serviceProvider) => { var httpContext = serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext; if (httpContext != null && httpContext.Request.Headers.TryGetValue("Authorization", out var authHeader)) { metadata.Add("Authorization", authHeader.ToString()); Console.WriteLine($"Sent Authorization: {authHeader}"); } return Task.CompletedTask; });
内容的提问来源于stack exchange,提问作者M. Ozn
相关产品推荐
相关产品推荐

