如何在JwtBearerEvents.OnAuthenticationFailed事件中强制认证成功,实现JWT认证失败时触发第二种认证方式
解决JWT认证失败时Fallback到第二种认证并强制认证成功的问题
我懂你现在的困扰——想在JWT认证失败的时候自动切换到第二种认证方式,并且尝试在OnAuthenticationFailed事件里手动标记认证成功,但目前的逻辑就是不生效对吧?咱们先聊聊问题出在哪,再给你可行的解决方案。
为什么你当前的代码不生效?
你现在的代码里只调用了c.Success()并清除了异常,但ASP.NET Core的认证中间件需要**有效的ClaimsPrincipal**来确认认证成功。仅仅调用Success()不足以让中间件认可认证状态,因为它没有拿到代表用户身份的主体信息。
方案一:改进OnAuthenticationFailed事件逻辑
你可以在事件里先完成第二种认证的验证,成功后手动构建ClaimsPrincipal,再标记认证成功。代码示例如下:
services.AddAuthentication(BearerAuthenticationDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = configuration.GetSection("AuthenticationConfiguration").GetValue<string>("Authority"); options.TokenValidationParameters = new TokenValidationParameters() { ValidateAudience = false }; options.Events = new JwtBearerEvents() { OnAuthenticationFailed = async context => { // 执行第二种认证逻辑,比如验证API密钥、自定义令牌等 bool isSecondAuthSuccess = await ExecuteSecondAuthentication(context.HttpContext); if (isSecondAuthSuccess) { // 构建认证成功的用户主体 var userClaims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "替换为第二种认证获取的用户ID"), // 添加其他业务需要的声明 }; var identity = new ClaimsIdentity(userClaims, "SecondAuthenticationScheme"); context.Principal = new ClaimsPrincipal(identity); // 清除异常并标记认证成功 context.Exception = null; context.Success(); } // 如果第二种认证也失败,就保持原有失败状态,无需额外操作 } }; }); // 模拟第二种认证的方法,你可以替换成自己的逻辑 private async Task<bool> ExecuteSecondAuthentication(HttpContext httpContext) { // 示例:从请求头获取API密钥并验证 string apiKey = httpContext.Request.Headers["X-API-Key"].FirstOrDefault(); return await ValidateApiKey(apiKey); } private async Task<bool> ValidateApiKey(string apiKey) { // 这里写你的验证逻辑,比如查询数据库、调用第三方验证服务等 return await Task.FromResult(apiKey == "你的有效API密钥"); }
这个方案的核心是:必须为context.Principal赋值,这是认证中间件判断用户是否通过认证的关键标识,再配合context.Success()就能正确触发认证成功的流程。
方案二:用PolicyScheme实现更优雅的Fallback逻辑
如果你想让两种认证方式的逻辑更解耦,符合ASP.NET Core认证框架的设计,推荐使用PolicyScheme来处理 fallback 逻辑。这种方式会先尝试JWT认证,失败后自动切换到第二种认证:
// 注册认证策略,作为默认认证方案 services.AddAuthentication("FallbackAuthPolicy") // 添加策略方案,用于选择要使用的认证方式 .AddPolicyScheme("FallbackAuthPolicy", "Fallback Authentication", options => { options.ForwardDefaultSelector = context => { // 优先尝试JWT认证(如果请求里有Bearer令牌) string authHeader = context.Request.Headers.Authorization.FirstOrDefault(); if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ")) { return JwtBearerDefaults.AuthenticationScheme; } // 否则直接使用第二种认证方式 return "SecondAuthScheme"; }; }) // 注册JWT认证 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.Authority = configuration.GetSection("AuthenticationConfiguration").GetValue<string>("Authority"); options.TokenValidationParameters = new TokenValidationParameters() { ValidateAudience = false }; options.Events = new JwtBearerEvents() { OnAuthenticationFailed = context => { // JWT认证失败时,让中间件继续尝试下一种认证方式 context.NoResult(); return Task.CompletedTask; } }; }) // 注册第二种认证方式 .AddScheme<SecondAuthOptions, SecondAuthHandler>("SecondAuthScheme", "Second Authentication", options => { // 这里可以添加第二种认证的配置选项 }); // 第二种认证的Handler实现 public class SecondAuthHandler : AuthenticationHandler<SecondAuthOptions> { public SecondAuthHandler(IOptionsMonitor<SecondAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { // 实现第二种认证逻辑 string apiKey = Request.Headers["X-API-Key"].FirstOrDefault(); if (string.IsNullOrEmpty(apiKey)) { return AuthenticateResult.Fail("API密钥缺失"); } if (await ValidateApiKey(apiKey)) { // 构建成功的认证票据 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "从第二种认证获取的用户ID") }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } return AuthenticateResult.Fail("无效的API密钥"); } private async Task<bool> ValidateApiKey(string apiKey) { // 替换成你的实际验证逻辑 return await Task.FromResult(apiKey == "有效API密钥"); } } // 第二种认证的配置选项类 public class SecondAuthOptions : AuthenticationSchemeOptions { // 可以添加自定义配置项,比如验证服务地址等 }
这种方式的优势在于:两种认证逻辑完全分离,代码更易维护和扩展,也更贴合ASP.NET Core认证的设计理念。
内容的提问来源于stack exchange,提问作者Elcio Santos
相关产品推荐
相关产品推荐

