AuthenticationMiddleware 如何处理多个方案

How does AuthenticationMiddleware handle multiple schemes

提问人:user22155685 提问时间:11/13/2023 更新时间:11/13/2023 访问量:68

问:

我们知道我们可以使用多种方案来授权用户,例如

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,
        options => builder.Configuration.Bind("JwtSettings", options))
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
        options => builder.Configuration.Bind("CookieSettings", options));

但根据代码:https://source.dot.net/#Microsoft.AspNetCore.Authentication/AuthenticationMiddleware.cs,50AuthenticationMiddleware

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);
}

似乎首先应用了多个方案。但是,它使用 not .foreachIAuthenticationRequestHandlerIAuthenticationHandler

因此,当我们调用扩展方法时,我们将处理程序类型的方案添加为 (https://source.dot.net/#Microsoft.AspNetCore.Authentication.Cookies/CookieExtensions.cs,81) 接口实现链为:AddCookieCookieAuthenticationHandler

public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions>

public abstract class SignInAuthenticationHandler<TOptions> : SignOutAuthenticationHandler<TOptions>, IAuthenticationSignInHandler

// ...

SignInAuthenticationHandler如果我们一直跟踪,则不会实现,那么如何处理多个方案并调用多个处理程序呢?据我所知,只应用了默认方案?IAuthenticationRequestHandlerAuthenticationMiddleware

C# ASP.NET 核心

评论

0赞 Dai 11/13/2023
ASP.NET Core 是...在支持多种方案时很复杂: 但我认为这篇文章应该回答你的问题: github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/security/...... (FWIW,有点红鲱鱼,因为它(从 ASP.NET Core 5.0 开始)目前仅由 HTTP 协商(NTLM、Kerberos 等)的处理程序实现,而不是任何库存身份验证库(Cookies、JWT 等)。IAuthenticationRequestHandler
0赞 Dai 11/13/2023
...但基本上,(AFAIK) 最新版本的 ASP.NET Core 不支持多个默认身份验证方案 - 但如果您确实想使用多个方案,您可以在 AuthenticationPolicy 和/或 .我目前正在处理一个 SPA+Web 服务项目,其中 SPA 使用 Cookie(因为 SPA 无法安全地使用 JWT),因此 Web 服务需要在(几乎)所有端点上接受 Cookie JWT 身份验证 - 我发现如果我实际删除任何默认方案并要求所有内容都是明确的(通过策略,而不是,那将是乏味的![AuthorizeAttribute][Authorize]
0赞 user22155685 11/13/2023
@Dai好的,你能给我看源代码 source.dot.net/#Microsoft.AspNetCore.Authentication/ 吗? 哪个代码块调用多个?AuthenticationMiddlewareIAuthenticationHandler
0赞 Dai 11/13/2023
请参阅我发布的答案,它回答了您的后续问题。

答:

3赞 Kahbazi 11/13/2023 #1

AuthenticationMiddleware 如何处理多个方案并调用多个处理程序?

不会的!如您所述,仅对默认方案进行身份验证。如果您有多个身份验证方案,则需要使用 和 。您可以设置策略必须使用的身份验证方案,并将其应用于终端节点。然后,将使用指定的方案对该端点进行身份验证。AuthenticationMiddlewareAuthorizationMiddlewarePolicyAuthorizationMiddleware

2赞 Dai 11/13/2023 #2

注意:这个答案是基于我对 ASP.NET Core 7.0.13 的程序集和源代码的阅读——它不适用于 ASP.NET Core 的早期版本,如 1.x、2.x 和 3.x(甚至可能是 5.x 甚至 6.x),其中 authX 系统设计经历了频繁的重新设计和迭代。


在评论中回应OP的后续问题:

好的,你能告诉我代码块调用多个的源代码吗AuthenticationMiddlewareIAuthenticationHandler

答案是“它没有”(基本上,与@Kahbazi在他的回答中所说的相同),但这必然导致我们问“那么,当您指定多个身份验证方案时,[授权]是如何工作的?...

...这个问题的答案在于另一个中间件,因为(令人惊讶的是),它并不是唯一执行请求身份验证的中间件AuthenticationMiddleware

第 1 点:您将有一个隐式或显式:AuthorizationPolicy

当您的属性列出了多个方案时,ASP.NET 会将该属性隐式转换为 (via ),每个方案都列在该策略的字符串集合中。[Authorize][Authorize( AuthenticationSchemes = "MyCookieScheme, MyJwtScheme, MyHttpBasicScheme" )]AuthorizationPolicyinterface IAuthorizeData.AuthenticationSchemes

在另一种情况下,您专门配置了一个直接(不使用 ),那么您也可以在其中设置多个方案名称。AuthorizationPolicy[Authorize].AuthenticationSchemes

第 2 点:实际上是由不同的中间件而不是由AuthorizationPolicyAuthenticationMiddleware

...相反,它由 .AuthorizationMiddleware

...因此,与直觉相反,_when您使用 2 个或更多身份验证方案,那么除了授权请求外,它们还将执行双重任务并验证请求。AuthorizationMiddleware

就在这里:https://github.com/dotnet/aspnetcore/blob/ae33890a62edb2fed5554efab597fb7fc41f8cbe/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs#L156C9-L156C9

public class AuthorizationMiddleware
{
    public async Task Invoke(HttpContext context)
    {
        // ...

        var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
        var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context); // <-- In here

        // ...
    }

遍历所有列出的循环位于 PolicyEvaluator 类中: IPolicyEvaluator:foreachAuthenticationSchemes

foreach (var scheme in policy.AuthenticationSchemes)
{
    var result = await context.AuthenticateAsync(scheme);
    if (result != null && result.Succeeded)
    {
        // ...
    }
}