ASP.NET 8中API证书认证与OpenID认证共存配置问题
ASP.NET 8 实现路由分离认证(AAD OpenID + 证书认证)
问题背景
现有ASP.NET 8应用使用AAD认证并通过AAD组管控访问权限,配置运行正常:
builder.Services .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration); builder.Services.AddAuthorization(options => { options.AddPolicy("Role_AdminPrivilege_Read", p => { p.RequireClaim("roles","AzureGroup_AdminPrivilege_Read"); }); });
需新增系统账户的API访问权限,这类账户无法通过AAD组授权,必须使用证书认证。但添加证书认证后原OpenID认证失效,需求为:/api路由使用证书认证,其余路由保留OpenID认证。
正确配置方案
1. 统一注册两种认证方案
避免重复调用AddAuthentication,在同一个配置块中设置默认认证/挑战方案,同时注册OpenID和证书认证:
// 配置默认认证/挑战方案为OpenID,适配普通路由 builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = OpenIdConnectDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) // 注册AAD OpenID认证 .AddMicrosoftIdentityWebApp(builder.Configuration) // 注册证书认证并自定义验证逻辑 .AddCertificate(options => { options.RevocationMode = X509RevocationMode.NoCheck; options.AllowedCertificateTypes = CertificateTypes.All; options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { var validationService = context.HttpContext.RequestServices.GetService<CertificateValidationService>(); if (validationService != null && validationService.ValidateCertificate(context.ClientCertificate)) { Console.WriteLine("Success"); context.Success(); } else { Console.WriteLine("invalid cert"); context.Fail("invalid cert"); } return Task.CompletedTask; } }; }); // 注册证书验证服务 builder.Services.AddSingleton<CertificateValidationService>();
2. 配置差异化授权策略
为普通路由和API路由分别创建授权策略,明确指定对应的认证方案:
builder.Services.AddAuthorization(options => { // 原有AAD组权限策略,绑定OpenID认证方案 options.AddPolicy("Role_AdminPrivilege_Read", p => { p.RequireClaim("roles", "AzureGroup_AdminPrivilege_Read"); p.AuthenticationSchemes.Add(OpenIdConnectDefaults.AuthenticationScheme); }); // API专用证书认证策略,绑定证书认证方案 options.AddPolicy("ApiCertificatePolicy", p => { p.RequireAuthenticatedUser(); p.AuthenticationSchemes.Add(CertificateAuthenticationDefaults.AuthenticationScheme); }); });
3. 路由层面绑定对应策略
方式一:控制器特性标注
- 普通控制器使用AAD组策略:
[Authorize(Policy = "Role_AdminPrivilege_Read")] public class HomeController : Controller { // 业务逻辑 }
- API控制器使用证书策略:
[Route("api/[controller]")] [ApiController] [Authorize(Policy = "ApiCertificatePolicy")] public class AdminApiController : ControllerBase { // API业务逻辑 }
方式二:全局路由约定(批量配置)
通过路由约定自动为/api开头的路由绑定证书策略,无需逐个控制器标注:
// API路由绑定证书认证策略 app.MapControllers() .RequireAuthorization("ApiCertificatePolicy") .Add(endpointFilter => async (context, next) => { // 仅对/api开头的路由应用策略 if (context.HttpContext.Request.Path.StartsWithSegments("/api")) { return await next(context); } return await endpointFilter.Invoke(context, next); }); // 普通路由绑定AAD组策略 app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}") .RequireAuthorization("Role_AdminPrivilege_Read");
4. Kestrel证书模式调整
使用DelayCertificate模式,既不强制所有请求提供证书,又能让API请求正常获取客户端证书:
builder.Services.Configure<KestrelServerOptions>(options => { options.ConfigureHttpsDefaults(httpsOptions => { httpsOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate; }); });
关键注意事项
- 禁止重复调用
AddAuthentication,否则会覆盖之前的认证配置。 - 必须为不同策略明确绑定对应认证方案,确保认证流程正确触发。
DelayCertificate模式是平衡OpenID登录流程和API证书需求的最优选择。
内容的提问来源于stack exchange,提问作者Stephan
相关产品推荐
相关产品推荐

