AuthenticationMiddleware如何处理多种身份验证方案?为何仅默认方案生效?
我们可以通过以下方式配置多种身份验证方案:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => builder.Configuration.Bind("JwtSettings", options)) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => builder.Configuration.Bind("CookieSettings", options));
但查看AuthenticationMiddleware的核心代码:
public async Task Invoke(HttpContext context) { // ... var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()) { var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler; if (handler != null && await handler.HandleRequestAsync()) { return; } } var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (defaultAuthenticate != null) { var result = await context.AuthenticateAsync(defaultAuthenticate.Name); // ... } await _next(context); }
这段代码里的foreach循环只处理实现了IAuthenticationRequestHandler的方案,而我们通过AddCookie添加的CookieAuthenticationHandler的继承链如下,并未实现该接口:
public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions> public abstract class SignInAuthenticationHandler<TOptions> : SignOutAuthenticationHandler<TOptions>, IAuthenticationSignInHandler // ...
那问题来了:AuthenticationMiddleware到底是如何处理多种身份验证方案并调用多个处理程序的?从代码看似乎只有默认方案会被执行?
核心结论
默认情况下,AuthenticationMiddleware确实只会自动执行默认验证方案的AuthenticateAsync逻辑。但这并不意味着其他方案完全没用,ASP.NET Core提供了多种方式触发非默认方案的验证。
具体解释
IAuthenticationRequestHandler的作用
这个接口是给那些需要主动接管请求流程的方案设计的(比如OAuth2的回调、OpenID Connect的登录回调),这类方案处理完请求后会直接返回,不会走后续中间件逻辑。而Cookie、JWT这类被动验证方案不需要实现它,它们只在被显式调用或通过策略指定时才会执行验证。非默认方案的触发方式
- 通过
[Authorize]特性指定方案:在控制器或Action上标注[Authorize(AuthenticationSchemes = "Cookie,Bearer")],此时认证服务会依次执行指定的所有方案的验证逻辑,只要有一个验证通过就算认证成功。 - 手动调用验证方法:在业务代码里显式调用
context.AuthenticateAsync("Cookie")或context.AuthenticateAsync("Bearer"),主动触发对应方案的验证流程。 - 通过授权策略指定多方案:在配置授权策略时,指定需要验证的多个方案,示例如下:
之后在builder.Services.AddAuthorization(options => { options.AddPolicy("CookieOrJwt", policy => { policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme); policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); policy.RequireAuthenticatedUser(); }); });[Authorize(Policy = "CookieOrJwt")]中使用该策略即可触发多方案验证。
- 通过
默认方案的意义
默认方案是无显式指定时的 fallback 选项:比如单独使用[Authorize]特性时,会自动使用默认方案执行验证;另外,SignInAsync、SignOutAsync这类方法在不指定方案时,也会使用对应的默认登录/登出方案(可通过AddAuthentication的参数设置默认方案,也可单独配置DefaultSignInScheme、DefaultSignOutScheme等细分选项)。
内容的提问来源于stack exchange,提问作者user22155685

