.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
相关产品推荐
相关产品推荐

