You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 14:34:54