我们知道我们可以使用多种方案对用户进行身份验证,例如

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

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

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,而不是IAuthenticationHandler.

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

public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions>

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

// ...

如果我们一直向上跟踪,SignInAuthenticationHandler就不会实现IAuthenticationRequestHandler,那么AuthenticationMiddleware如何处理多个方案和调用多个处理程序呢?据我所知,只有默认方案才适用吗?

推荐答案

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


在 comments 中回答行动组的后续问题:

好的,你能给我看一下代码块调用多个IAuthenticationHandlerAuthenticationMiddleware源代码吗

答案是"不会"(基本上是the same as what @Kahbazi says in his answer),但这必然会让我们问"so how does 101 work when you specify multiple authentication schemes?"……

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

Point 1: You'll have an implicit or explicit AuthorizationPolicy:

如果您有一个列出了多个方案的[Authorize]属性,如[Authorize( AuthenticationSchemes = "MyCookieScheme, MyJwtScheme, MyHttpBasicScheme" )],则ASP.NET将隐式地将该属性转换为AuthorizationPolicy(通过interface IAuthorizeData),每个方案都列在该策略的.AuthenticationSchemes字符串集合中.

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

Point 2: The AuthorizationPolicy is actually evalauted by a different middleware and not by AuthenticationMiddleware

……取而代之的是AuthorizationMiddleware.

因此,counter-intuitively,_当您使用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

        // ...
    }

以及迭代所有列出的AuthenticationSchemes is in class PolicyEvaluator : IPolicyEvaluatorforeach循环:

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

Csharp相关问答推荐

在包含空项的列表上使用具有断言T的摘要表

有没有方法让ASP.NET Core模型绑定器使用私有设置器来设置属性?

为什么.Equals(SS,StringComparison. ClientCultureIgnoreCase)在Net 4.8和6.0之间不同?

无法使用ternal- .net修复可空警告

如何禁用ASP.NET MVP按钮,以便无法使用开发人员控制台重新启用它

使用C#中的Shape API从Azure目录获取所有用户

为什么在ANTLR4中会出现不匹配的输入错误?""

如何在C#中使用正则表达式抓取用逗号分隔的两个单词?

HttpConext.Request.Path和HttpConext.GetEndpoint()之间的差异

如何在C#中从正则表达式中匹配一些数字但排除一些常量(也是数字)

使用可信第三方的Iext8.Net pdf签名

为什么EventInfo.EventHandlerType返回可为空的Type值?

Razor视图Razor页面指向同一端点时的优先级

将字节转换为 struct 并返回

在IAsyncEnumerable上先调用,然后跳过(1)可以吗?

Linux Docker上的.NET6在某个时间抛出后,在加密操作期间发生System.Security.Cryptography.CryptographicException:错误

MSTest--将消息直接写入StdOut和使用TestContext有什么不同?

Postgres ENUM类型在第一次运行时对Dapper不可见

在.NET8中如何反序列化为私有字段?

使用C#12中的主构造函数进行空判断