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

.NET Core中JWT与客户端证书双重认证共存问题求助

解决方案:JWT Bearer与客户端证书双重认证实现

要实现客户端证书+JWT的双重强制认证,你需要自定义授权策略——直接在[Authorize]中指定多个认证方案是“任一通过即可”,而我们需要的是“两者都必须通过”,具体实现步骤如下:

步骤1:添加自定义授权策略

在Startup.ConfigureServices中注册授权策略,强制要求同时通过两种认证方案的验证:

services.AddAuthorization(options =>
{
    options.AddPolicy("RequireBothCertificateAndJwt", policy =>
    {
        // 指定需要验证的两种认证方案
        policy.AddAuthenticationSchemes(
            JwtBearerDefaults.AuthenticationScheme, 
            CertificateAuthenticationDefaults.AuthenticationScheme);
        // 要求用户必须同时通过两种认证的身份验证
        policy.RequireAuthenticatedUser();
    });
});

步骤2:修改控制器的授权属性

将控制器原有的[Authorize(AuthenticationSchemes = "Certificate,Bearer")]替换为自定义策略:

[Authorize(Policy = "RequireBothCertificateAndJwt")]
public IEnumerable<WeatherForecast> Get()
{
    var rng = new Random();
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
}

步骤3:完整修改后的Startup代码

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

namespace WebApiKeycloakAndCertificate
{
  public class Startup
  {
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApiKeycloak", Version = "v1" });
        });
        services.AddTransient<CertificateValidation>();
        
        // 注册认证服务
        services.AddAuthentication(authOptions =>
        {
            authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(jwtOptions =>
        {
            jwtOptions.Authority = "https://dev.company.com/auth/realms/my-realm";
            jwtOptions.Audience = "account";

            jwtOptions.SaveToken = false;
            jwtOptions.IncludeErrorDetails = true;

            jwtOptions.RequireHttpsMetadata = true;
            
            jwtOptions.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = true,
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidIssuer = "https://dev.company.com/auth/realms/my-realm/protocol/openid-connect/token",
                ValidateLifetime = true,
            };
        }).AddCertificate(options => {
            options.AllowedCertificateTypes = CertificateTypes.All;
            options.Events = new CertificateAuthenticationEvents
            {
                OnCertificateValidated = context => {
                    var validationService = context.HttpContext.RequestServices.GetService<CertificateValidation>();
                    if (validationService.ValidateCertificate(context.ClientCertificate))
                    {
                        context.Success();
                    }
                    else
                    {
                        context.Fail("Invalid certificate");
                    }
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = context => {
                    context.Fail("Invalid certificate");
                    return Task.CompletedTask;
                }
            };
        });

        // 添加自定义双重认证策略
        services.AddAuthorization(options =>
        {
            options.AddPolicy("RequireBothCertificateAndJwt", policy =>
            {
                policy.AddAuthenticationSchemes(
                    JwtBearerDefaults.AuthenticationScheme, 
                    CertificateAuthenticationDefaults.AuthenticationScheme);
                policy.RequireAuthenticatedUser();
            });
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApiKeycloak v1"));
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
  }
}

原理说明

  • 默认的AuthenticationSchemes参数是逻辑或关系:只要其中一种认证方案通过,就允许访问。
  • 自定义授权策略通过AddAuthenticationSchemes绑定两种方案,并结合RequireAuthenticatedUser(),实现逻辑与的强制双重验证——只有当JWT和客户端证书都验证成功时,才会授予接口访问权限。

内容的提问来源于stack exchange,提问作者ktary

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:27:47