.NET 6 Web API同时支持JWT与API Key认证遇401错误求助
搞定JWT+API Key双认证的401问题
问题根源
你当前的配置里,默认认证和挑战方案都是JWT Bearer。当使用不带Bearer前缀的API Key请求时,JWT认证会直接失败,触发默认的401错误。同时你自定义的中间件逻辑存在多处问题,导致API Key认证无法正常生效。
一步步解决方法
1. 修改认证配置,支持双认证方案
调整Startup.cs中的认证服务配置,添加API Key认证方案,并让系统自动判断使用哪种认证方式:
services.AddAuthentication(options => { // 设置默认认证为"多方案",让系统依次尝试JWT和API Key options.DefaultAuthenticateScheme = "MultiScheme"; options.DefaultChallengeScheme = "MultiScheme"; }) // 保留原有JWT认证配置 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters = new TokenValidationParameters { // 你的JWT验证参数,示例如下 ValidateIssuer = true, ValidIssuer = "你的Issuer地址", ValidateAudience = true, ValidAudience = "你的Audience标识", ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的密钥字符串")) }; }) // 添加API Key认证方案,后续用自定义处理器实现逻辑 .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ApiKeyScheme", options => { }); // 配置多方案的路由逻辑 services.AddAuthentication() .AddPolicyScheme("MultiScheme", "MultiScheme", options => { options.ForwardDefaultSelector = context => { var authHeader = context.Request.Headers["Authorization"].FirstOrDefault(); // 带Bearer前缀的请求用JWT,其他用API Key return !string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ") ? JwtBearerDefaults.AuthenticationScheme : "ApiKeyScheme"; }; });
2. 实现API Key认证处理器(替代自定义中间件)
用ASP.NET Core标准的认证处理器替代原自定义中间件,更符合框架规范:
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions> { private readonly IAuthService _authService; public ApiKeyAuthenticationHandler( IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IAuthService authService) : base(options, logger, encoder, clock) { _authService = authService; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { // 读取Authorization请求头 if (!Request.Headers.TryGetValue("Authorization", out var authHeader)) { return AuthenticateResult.NoResult(); } string apiKey = authHeader.ToString().Trim(); if (string.IsNullOrEmpty(apiKey)) { return AuthenticateResult.Fail("API Key不能为空"); } // 校验API Key是否为合法GUID格式 if (!Guid.TryParse(apiKey, out var apiKeyGuid)) { return AuthenticateResult.Fail("API Key格式无效"); } // 复用你原有的API Key有效性校验逻辑 if (!IsValidPat(apiKey)) { return AuthenticateResult.Fail("API Key无效"); } // 从数据库获取用户关联的Claims var claims = await _authService.GetClaimsForPat(apiKeyGuid); if (claims == null) { return AuthenticateResult.Fail("该API Key未关联任何用户"); } // 组装用户身份Claims var claimsToInclude = new List<Claim>(claims) { new Claim(ClaimTypes.AuthenticationMethod, "ApiKey") // 这里添加你需要的额外自定义Claims }; var identity = new ClaimsIdentity(claimsToInclude, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } // 复制你原有的IsValidPat方法逻辑 private bool IsValidPat(string token) { // 示例:检查API Key是否存在于数据库 return true; } } // 配套的认证选项类,无需修改内容 public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions { }
3. 调整中间件顺序与授权策略
移除原自定义中间件,恢复标准的中间件执行顺序:
app.UseCors(ourOrigins); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
同时确保PatAuthorization授权策略已在ConfigureServices中正确配置:
services.AddAuthorization(options => { options.AddPolicy("PatAuthorization", policy => { // 示例:要求用户拥有指定权限Claim policy.RequireClaim("Permission", "AccessApi"); }); });
4. 控制器指定支持的认证方案
在需要支持双认证的控制器或Action上,标注允许的认证方案:
[Authorize(AuthenticationSchemes = "MultiScheme")] [ApiController] [Route("api/[controller]")] public class YourController : ControllerBase { // 针对特定Action可单独指定授权策略 [Authorize(Policy = "PatAuthorization")] [HttpGet] public IActionResult Get() { return Ok(); } }
原自定义中间件的问题点
如果坚持使用原中间件,需修复以下问题:
- 中间件顺序错误:
PatAuthenticationMiddleware必须放在UseAuthentication之前,否则JWT认证失败会直接返回401,中间件无法触发。 - User对象处理错误:JWT认证失败时
context.User是匿名用户,需新建ClaimsPrincipal而非往现有User中添加Identity。 - 授权逻辑位置错误:不要在中间件中提前返回403,应交给
UseAuthorization中间件处理,符合ASP.NET Core的授权流程。
内容的提问来源于stack exchange,提问作者Pan Michal
相关产品推荐
相关产品推荐

