.NET Core 3.0指定认证Scheme无效:默认Scheme仍被触发
首先得明确你遇到这个问题的核心原因:当指定的认证Scheme Handler返回AuthenticateResult.NoResult()而非AuthenticateResult.Fail()时,ASP.NET Core认证中间件会认为该Scheme无法处理当前请求,进而自动尝试其他已注册的Scheme(包括你设置的默认Scheme)。另外也有可能是你的默认Scheme Handler逻辑存在漏洞,比如无有效凭证时也返回认证成功。
下面给你几个针对性的解决方案:
方案1:修正认证Handler的返回结果
这是最直接的解决方式,修改你的IdentityAuthenticationHandler,在认证失败时返回Fail()而非NoResult(),让中间件直接终止认证流程,不再尝试其他Scheme。示例代码如下:
protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // 从请求中提取凭证(示例逻辑,根据你的实际场景调整) var authHeader = Request.Headers["Authorization"].FirstOrDefault(); if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Identity ")) { // 认证失败,返回Fail,终止后续认证尝试 return Task.FromResult(AuthenticateResult.Fail("Missing or invalid IdentityScheme credentials")); } // 验证凭证逻辑(省略具体实现) if (!ValidateIdentityCredentials(authHeader.Substring(9))) { return Task.FromResult(AuthenticateResult.Fail("Invalid IdentityScheme credentials")); } // 认证成功,生成认证票据 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "valid-user-id") }; var identity = new ClaimsIdentity(claims, AuthConstants.IdentityScheme); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, AuthConstants.IdentityScheme); return Task.FromResult(AuthenticateResult.Success(ticket)); }
方案2:通过Policy严格锁定认证Scheme
如果你更倾向于用Policy来管控,可以调整Policy的配置,明确将认证Scheme列表设置为仅包含IdentityScheme,避免默认Scheme被自动纳入:
services.AddAuthorization(options => { options.AddPolicy("IdentityAuthPolicy", policy => { // 直接赋值而非Add,确保列表中只有指定的Scheme policy.AuthenticationSchemes = new List<string> { AuthConstants.IdentityScheme }; policy.RequireAuthenticatedUser(); }); });
然后在控制器上使用这个Policy:
[Authorize(Policy = "IdentityAuthPolicy")] [ApiController] [Route("[controller]")] public class UsersController : ControllerBase {}
注意:这个方案需要配合方案1的Handler修正才能彻底生效,否则如果Handler返回NoResult(),中间件还是会尝试其他Scheme。
方案3:根源禁用默认Scheme的自动 fallback(可选)
如果你的应用中大部分控制器都不需要默认Scheme的兜底,可以在注册认证服务时不设置默认Scheme,仅针对需要默认Scheme的控制器单独指定:
services.AddAuthentication() // 不设置默认Scheme .AddScheme<AuthenticationSchemeOptions, DefaultSchemeHandler>(AuthConstants.DefaultScheme, AuthConstants.DefaultScheme, null) .AddScheme<AuthenticationSchemeOptions, IdentityAuthenticationHandler>(AuthConstants.IdentityScheme, AuthConstants.IdentityScheme, null);
然后在需要使用默认Scheme的控制器上明确指定:
[Authorize(AuthenticationSchemes = AuthConstants.DefaultScheme)] public class PublicController : ControllerBase {}
这种方式可以从根源上避免默认Scheme被自动尝试,但需要确保所有控制器都明确指定了认证Scheme,否则会抛出无默认Scheme的异常。
验证方式
修改后,你可以在两个Handler的HandleAuthenticateAsync方法中添加日志输出,当请求UsersController且未携带有效IdentityScheme凭证时,应该只有IdentityAuthenticationHandler被调用,且直接返回401 Unauthorized,不会触发DefaultSchemeHandler的逻辑。
内容的提问来源于stack exchange,提问作者Egidijus Brazaitis

