【问题标题】:What exactly is 'UseAuthentication()' for?'UseAuthentication()' 到底是干什么用的?
【发布时间】:2018-07-27 22:45:17
【问题描述】:

我有一个关于 ASP.NET Core 2 中的身份验证的问题:调用 app.UseAuthentication() 到底是为了什么?

这是实现自定义身份验证逻辑的基本先决条件吗?我已经看过UseAuthentication 和实际中间件AuthenticationMiddleware 的实现,但老实说,我不明白它实际上在做什么以及为什么它是必要的。

换一种说法:

我需要调用 UseAuthentication()

或者它是一个很好的东西,我可以做我的自定义身份验证吗?

如果我不调用 UseAuthentication() 就可以了,我仍然会对AuthenticationMiddleware 实际在做什么感兴趣。因此,如果您知道,如果您也能为我解释一下,我将不胜感激。

【问题讨论】:

    标签: c# authentication asp.net-core-2.0 asp.net-core-middleware


    【解决方案1】:

    你确实需要调用它。

    UseAuthentication() 记录为:

    https://docs.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”,对吗?
    • 这不是真的,因为这里OP写了一个自定义的中间件,从而绕过了认证中间件。
    【解决方案2】:

    如果您编写自定义中间件(就像您在示例中所做的那样),则无需调用 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 */ })
    }
    

    【讨论】:

    • 这个答案是指AddAuthentication,但OP询问的是UseAuthenticationAddAuthentication 将身份验证服务添加到服务集合中,而UseAuthentication 将 .NET Core 的身份验证中间件添加到管道中。如果您有自己的自定义中间件,则不需要UseAuthentication
    【解决方案3】:

    虽然这是一个旧线程,但由于我最近偶然发现了同样的问题,我认为更多地了解内部原理可能会使其他人受益

    简短的回答取决于您的服务类型和 API。您不需要在以下情况下需要致电UseAuthentication

    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.* 中移动为通用服务而不是与中间件耦合的原因。

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

    这解释了middleware 代码的第二部分:

    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 授权流程中,这发生在调用 IAuthenticationService.AuthenticateAsync 的 AuthorizeFilter 类中

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

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

    【讨论】:

    • 这应该是例外的答案!谢谢......你让我从研究和翻阅 github 上的代码中节省了很多。
    【解决方案4】:

    来自UseAuthentication的GitHub源代码。

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

    【讨论】:

      猜你喜欢
      • 2016-12-29
      • 1970-01-01
      • 2020-05-03
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-19
      相关资源
      最近更新 更多