.NET 7 gRPC服务配置客户端证书必填未生效,兼问双认证可行性
同时使用客户端证书与JWT令牌的gRPC服务认证问题
我有一套gRPC服务与客户端,希望通过客户端证书实现仅指定应用可调用服务,防止第三方开发的应用调用;同时用JWT令牌识别用户,区分不同用户的权限。
我的gRPC服务基于.NET 7的ASP Core应用托管,以下是program.cs代码:
Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; var builder = WebApplication.CreateBuilder(args); var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); try { builder.WebHost.ConfigureKestrel((context, options) => { string miStrCertificado = File.ReadAllText(builder.Configuration.GetSection("Certificados:Certificado").Value!); string miStrKey = File.ReadAllText(builder.Configuration.GetSection("Certificados:Key").Value!); X509Certificate2 miCertficadoX509 = X509Certificate2.CreateFromPem(miStrCertificado, miStrKey); X509Certificate2 miCertificado2 = new X509Certificate2(miCertficadoX509.Export(X509ContentType.Pkcs12)); miCertficadoX509.Dispose(); options.ListenAnyIP(Convert.ToInt32(builder.Configuration.GetSection("Servidor:Puerto").Value!), listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps(miCertificado2); }); options.ConfigureHttpsDefaults(miHttpsOptions => { miHttpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; miHttpsOptions.ServerCertificate = miCertificado2; }); }); builder.Services.AddHttpContextAccessor(); builder.Services.AddGrpc(); builder.Services.AddCodeFirstGrpc(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(builder.Configuration.GetSection("JwtAuthApp.Server:JwtTokenService:ClaveCifradoToken").Value!)), RequireExpirationTime = true, RequireSignedTokens = true, ClockSkew = TimeSpan.FromSeconds(10), ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapGrpcService<MyGrpcService>(); await app.RunAsync(); } catch (Exception ex) { Console.Write($"ERROR: {ex.Message}"); }
如上述代码所示,我已在Kestrel的ConfigureHttpsDefaults中设置了ClientCertificateMode.RequireCertificate,但测试时客户端未携带证书仍能收到服务响应,预期会触发认证异常。请问是否无法同时使用客户端证书与JWT令牌这两种认证方式?
解决方案:完全可以同时使用两种认证方式,问题出在配置逻辑缺失
1. 修正Kestrel客户端证书配置优先级问题
你在ListenAnyIP中单独调用UseHttps(miCertificado2)会覆盖ConfigureHttpsDefaults中的客户端证书要求,需要将证书验证逻辑直接嵌入监听配置:
builder.WebHost.ConfigureKestrel((context, options) => { string miStrCertificado = File.ReadAllText(builder.Configuration.GetSection("Certificados:Certificado").Value!); string miStrKey = File.ReadAllText(builder.Configuration.GetSection("Certificados:Key").Value!); X509Certificate2 miCertficadoX509 = X509Certificate2.CreateFromPem(miStrCertificado, miStrKey); X509Certificate2 miCertificado2 = new X509Certificate2(miCertficadoX509.Export(X509ContentType.Pkcs12)); miCertficadoX509.Dispose(); options.ListenAnyIP(Convert.ToInt32(builder.Configuration.GetSection("Servidor:Puerto").Value!), listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps(httpsOptions => { httpsOptions.ServerCertificate = miCertificado2; // 直接在监听配置中要求客户端证书 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; }); }); });
2. 注册客户端证书认证服务并配置双重认证策略
仅要求证书还不够,需要将客户端证书纳入ASP.NET Core认证体系,与JWT形成双重验证:
// 注册双重认证服务 builder.Services.AddAuthentication() // JWT认证配置 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(builder.Configuration.GetSection("JwtAuthApp.Server:JwtTokenService:ClaveCifradoToken").Value!)), RequireExpirationTime = true, RequireSignedTokens = true, ClockSkew = TimeSpan.FromSeconds(10), ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, }; // 适配gRPC的令牌提取逻辑 options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Headers.Authorization.FirstOrDefault()?.Split(" ").Last(); if (!string.IsNullOrEmpty(token)) { context.Token = token; } return Task.CompletedTask; } }; }) // 客户端证书认证配置 .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { // 验证证书是否为你指定的合法证书(示例:校验指纹) var validThumbprints = builder.Configuration.GetSection("Certificados:ValidThumbprints").Get<string[]>(); if (validThumbprints != null && validThumbprints.Contains(context.ClientCertificate.Thumbprint)) { var claims = new[] { new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject), new Claim("CertificateThumbprint", context.ClientCertificate.Thumbprint) }; context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); context.Success(); } else { context.Fail("无效的客户端证书"); } return Task.CompletedTask; } }; }); // 配置授权策略:要求同时通过两种认证 builder.Services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme, CertificateAuthenticationDefaults.AuthenticationScheme) .Build(); });
3. 为gRPC服务添加授权标记
确保你的gRPC服务类上添加[Authorize]特性,强制应用双重认证策略:
[Authorize] public class MyGrpcService : MyGrpc.MyGrpcBase { // 你的服务实现方法 }
关键说明
- 客户端证书认证:在TLS握手阶段完成,确保只有持有合法证书的应用能建立连接,实现应用级访问控制。
- JWT令牌认证:在应用层完成,识别具体用户并验证权限,实现用户级访问控制。
- 两种认证方式互不冲突,通过授权策略可强制要求请求同时满足两种认证条件,实现双重验证。
内容的提问来源于stack exchange,提问作者Álvaro García
相关产品推荐
相关产品推荐

