.NET Core 3.1 Web API如何实现Azure AD Token与客户端证书双认证
没问题,在.NET Core 3.1里完全可以实现这种双认证共存的模式,不用拆分现有Web应用,我给你梳理下具体的实现步骤和代码示例:
核心思路
我们要在同一个API里同时注册两种认证方案:
- 保留你现有的Azure AD JWT Token认证(
AddProtectedWebApi) - 添加客户端证书认证方案
然后通过自定义授权策略,允许用户通过任意一种认证方式访问API。
具体实现步骤
1. 配置两种认证方案
在Startup.cs的ConfigureServices方法里,先保留你原有的Azure AD配置,再添加证书认证的配置:
public void ConfigureServices(IServiceCollection services) { // 1. 保留现有Azure AD JWT认证配置 services.AddProtectedWebApi(options => { // 你的原有配置,比如租户ID、API客户端ID等 options.TenantId = "your-tenant-id"; options.ClientId = "your-api-client-id"; // 其他Azure AD相关配置... }); // 2. 添加客户端证书认证 services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; // 自定义证书验证逻辑 options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { var clientCert = context.ClientCertificate; // 这里可以根据你的需求添加验证规则:比如检查颁发者、有效期、证书主题等 if (clientCert.Issuer.Equals("your-trusted-issuer", StringComparison.OrdinalIgnoreCase) && clientCert.NotAfter >= DateTime.UtcNow) { // 将证书信息转换为标准ClaimsPrincipal,和JWT认证的用户格式统一 var claims = new[] { new Claim(ClaimTypes.NameIdentifier, clientCert.SubjectName.Name), new Claim(ClaimTypes.Name, clientCert.SubjectName.Name), // 可自定义添加角色或其他声明 new Claim(ClaimTypes.Role, "Cert-Authenticated-User") }; context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); context.Success(); } else { context.Fail("Invalid client certificate"); } return Task.CompletedTask; } }; }); // 3. 配置默认授权策略:允许两种认证方案任一通过 services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder( JwtBearerDefaults.AuthenticationScheme, CertificateAuthenticationDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); // 可选:给特定接口配置专属策略(比如某些接口只允许证书认证) options.AddPolicy("OnlyCertificate", policy => policy.RequireAuthenticatedUser() .AddAuthenticationSchemes(CertificateAuthenticationDefaults.AuthenticationScheme)); }); // 其他服务配置... services.AddControllers(); }
2. 调整中间件顺序和Kestrel配置
在Configure方法里确保认证、授权中间件的顺序正确,同时配置Kestrel接受可选的客户端证书(因为3.1没有5.0的Optional证书功能,我们用AllowCertificate让Kestrel接受但不强制要求证书):
2.1 Startup.cs的Configure方法
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); // 必须先添加认证中间件,再添加授权中间件 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
2.2 配置Kestrel接受可选证书
可以在appsettings.json里添加配置:
"Kestrel": { "Endpoints": { "Https": { "Url": "https://localhost:5001", "SslProtocols": "Tls12", "ClientCertificateMode": "AllowCertificate" } } }
或者在Program.cs里代码配置:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { options.ListenAnyIP(5001, listenOptions => { listenOptions.UseHttps(httpsOptions => { httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate; }); }); }); webBuilder.UseStartup<Startup>(); });
效果说明
配置完成后,你的API就能同时支持两种认证方式:
- 当前端在请求头里携带
Authorization: Bearer {JWT-Token}时,会自动走Azure AD的JWT认证流程 - 当前端携带客户端证书发起请求时,会触发证书认证逻辑,验证通过后即可访问API
- 如果你给某些接口标记了
[Authorize(Policy = "OnlyCertificate")],则该接口仅接受证书认证的请求
内容的提问来源于stack exchange,提问作者Ch0ufleur
相关产品推荐
相关产品推荐

