“UseAuthentication()”是用来做什么的?

69

我有一个关于ASP.NET Core 2身份验证的问题: app.UseAuthentication() 的含义是什么?

这是一种基本的前提条件,使我能够实现自定义身份验证逻辑吗?我已经查看了UseAuthentication的实现以及实际中间件AuthenticationMiddleware,但说实话,我不明白它到底在做什么以及为什么需要。

换句话说:

我需要调用UseAuthentication()吗? enter image description here

或者只是很好用,我也可以使用自定义身份验证? enter image description here

如果我没有调用UseAuthentication(),我仍然想知道AuthenticationMiddleware实际上在做什么。如果您知道,我将不胜感激。

4个回答

32

虽然这是一个旧的线程,但由于我最近遇到了同样的问题,我认为在内部方面提供更多光芒可能有益于其他人

简短的答案是取决于您的服务类型和API。当:

  1. 您实现自己的中间件来处理身份验证时-无需在此展开。您自己处理所有内容,显然不需要额外的依赖项
  2. 您不需要自动或远程身份验证

远程身份验证

需要重定向到标识提供商(如OpenID Connect)的身份验证。

有什么特别之处?

这些中间件需要关联不同的http调用。

首先,中间件处理初始调用,然后将其重定向到标识提供程序(用户需要登录),然后返回到中间件。 在这种情况下,中间件需要拥有请求,并且不允许其他身份验证中间件参与该过程。

这是中间件代码的第一部分:

middleware

// Give any IAuthenticationRequestHandler schemes a chance to handle the request
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;
    }
}
  • 当然,这只是一个简化的解释,远程处理程序更加复杂。最终的目标是专注并解释中间件的行为。

自动身份验证

默认方案下自动运行的身份验证。正如其名称所示,如果您定义了默认身份验证方案,则与中间件关联的身份验证处理程序将始终运行。

直观上,您会期望身份验证中间件首先运行,特别是它们应该在MVC层(即控制器)之前运行。但是,这也意味着身份验证层不知道哪些控制器应该运行或这些控制器的授权要求,换句话说,它不知道需要评估的授权策略[Authorize("Policy")]是什么。

因此,逻辑上,我们希望首先评估策略,然后再运行身份验证逻辑。这就是为什么身份验证处理程序在ASP 2.*中转移到通用服务而不是耦合到中间件中的原因。

但是,在某些情况下,您始终希望运行身份验证处理程序,而不管您的策略如何。在这种情况下,您可以定义默认身份验证方案,这些方案将自动运行。

这解释了中间件代码的第二部分:

var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
    var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
    if (result?.Principal != null)
    {
        context.User = result.Principal;
    }
}

如果您正在开发支持多个身份验证方案或具有经过身份验证和非身份验证控制器混合的REST API,则不需要自动身份验证,因为它会增加冗余。

结论

这带来了一个有趣的问题和答案:当身份验证不是自动的且不是远程时,何时何地进行身份验证?

在正常的MVC授权流程中,这发生在AuthorizeFilter类中,该类调用IAuthenticationService.AuthenticateAsync

  • 如果实现自己的授权层或使用较低级别的API(例如未实现为控制器的WebSockets)时,可以自己调用此方法。

对于这些情况,不需要调用UseAuthentication


这应该是期望的答案!谢谢...你刚刚帮我省了很多在Github上研究和查找代码的时间。 - chrsi

25

如果您编写自定义中间件(就像在示例中所做的那样),则不需要调用AddAuthentication,因为身份验证中间件不会意识到您自己的中间件。

话虽如此,您可能不想创建自己的中间件:您可能想创建一个新的身份验证处理程序,以便与ASP.NET身份验证框架良好地配合使用(这样您可以在控制器上使用[Authorize]属性)。

要创建自定义身份验证,必须创建继承自AuthenticationHandler并实现相关方法的专用处理程序。您可以查看关于GitHub上基本身份验证的示例:https://github.com/blowdart/idunno.Authentication,但以下是一个快速示例,以展示自定义处理程序的主旨。

public class BasicAuthenticationOptions : AuthenticationSchemeOptions
{
    public BasicAuthenticationOptions()
    {
    }
}

internal class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
    private const string _Scheme = "MyScheme";

    public BasicAuthenticationHandler(
        IOptionsMonitor<BasicAuthenticationOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string authorizationHeader = Request.Headers["Custom-Auth-Handler"];

        // create a ClaimsPrincipal from your header
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, "My Name")
        };

        var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
        var ticket = new AuthenticationTicket(claimsPrincipal,
            new AuthenticationProperties { IsPersistent = false },
            Scheme.Name
        );
        
        return AuthenticateResult.Success(ticket);
    }
}

然后你可以在 Startup.cs 中注册你的新方案:

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
        .AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("MyScheme", options => { /* configure options */ })
}

16
这个答案提到了 AddAuthentication,但是问题的提问者询问的是 UseAuthenticationAddAuthentication会将身份验证服务添加到服务集合中,而UseAuthentication会将.NET Core的身份验证中间件添加到管道中。如果您有自己的自定义中间件,则不需要使用UseAuthentication - Dominus.Vobiscum

4
你需要调用它。 UseAuthentication()的文档如下:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.authappbuilderextensions.useauthentication?view=aspnetcore-2.0

将AuthenticationMiddleware添加到指定的IApplicationBuilder中,从而启用身份验证功能。

它基本上做了这个:
IApplicationBuilder AddAuthentication(this IApplicationBuilder app) {

    return app.UseMiddleware<AuthenticationMiddleware>();
}

...所以它只是帮你省去了一些打字和可能需要额外的using导入。

这将在进程的请求处理管道中添加一个AuthenticationMiddleware实例,而这个特定的对象会为身份验证添加必要的组件。


好的。这意味着您将始终调用“UseAuthentication()”。要实现自定义身份验证,您将设置自定义的“IAuthenticationSchemeProvider”,对吗? - baumgarb
1
这并不是真的,因为这里OP编写了一个自定义中间件,从而绕过了身份验证中间件。 - Métoule

2

From the GitHub source code of UseAuthentication.

    public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }
        
        return app.UseMiddleware<AuthenticationMiddleware>();
    }

正如您所看到的,它只是添加了一个名为AuthenticationMiddleware的中间件。

这正是AuthenticationMiddleware所做的事情:

 public async Task Invoke(HttpContext context)
        {
            context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
            {
                OriginalPath = context.Request.Path,
                OriginalPathBase = context.Request.PathBase
            });

            // Give any IAuthenticationRequestHandler schemes a chance to handle the request
            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);
                if (result?.Principal != null)
                {
                    context.User = result.Principal;
                }
                if (result?.Succeeded ?? false)
                {
                    var authFeatures = new AuthenticationFeatures(result);
                    context.Features.Set<IHttpAuthenticationFeature>(authFeatures);
                    context.Features.Set<IAuthenticateResultFeature>(authFeatures);
                }
            }

            await _next(context);
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接