【问题标题】:ASP.NET Core - getting a message back from AuthenticationHandlerASP.NET Core - 从 AuthenticationHandler 获取消息
【发布时间】:2019-04-24 16:14:04
【问题描述】:

我已经实现了AuthenticationHandler 的子类。它返回AuthenticationResult.Fail("This is why you can't log in");

我本来希望这条消息最终出现在正文中,或者至少出现在 HTTP 状态文本中,但我得到的是一个空白的 401 响应。

有没有办法为 ASP.NET 核心中的失败身份验证尝试提供额外信息?

【问题讨论】:

    标签: asp.net authentication asp.net-core


    【解决方案1】:

    覆盖 HandleChallengeAsync:

    在下面的示例中,failReason 是我的 AuthenticationHandler 实现中的一个私有字段。 我不知道这是否是传递失败原因的最佳方式。但是在我的测试中,AuthenticateResult.Fail 方法上的AuthenticationProperties 并没有通过HandleChallengeAsync

    public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
    {
        private string failReason;
    
        public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
            , ILoggerFactory logger
            , UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { }
    
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            failReason = "Reason for auth fail";
            return AuthenticateResult.Fail(failReason);
        }
    
        protected override Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.StatusCode = 401;
    
            if (failReason != null)
            {
                Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = failReason;
            }
    
            return Task.CompletedTask;
        }
    }
    

    来自文档:https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-2.2

    如果有问题的身份验证方案将身份验证交互作为其请求流的一部分进行处理,请覆盖此方法以处理 401 质询问题。 (例如添加响应标头,或将登录页面或外部登录位置的 401 结果更改为 302。)

    来源: https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs#L201

    【讨论】:

    • 将字符串传递给 AuthenticateResult.Fail 的目的是什么?是不是什么都没有使用?
    • @Omiron 似乎是这种设计方式 - 消息字符串将用于内部日志记录目的。请参阅github.com/aspnet/Mvc/issues/4865 公平地说,401 是不言自明的,尽管在 Web 浏览器的情况下我想要返回一条消息而不是一个空白的 HTML 文档。
    【解决方案2】:

    我在自定义中间件中使用此代码返回 problemDetails 响应。

    public async Task Invoke(HttpContext httpContext)
    {
        await this.Next(httpContext);
    
        if (httpContext.Response.StatusCode == StatusCodes.Status401Unauthorized)
        {
            var authenticateResult = await httpContext.AuthenticateAsync();
    
            if (authenticateResult.Failure != null)
            {
                var routeData = httpContext.GetRouteData() ?? new RouteData();
                var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
    
                var problemDetails = this.ProblemDetailsFactory.CreateProblemDetails(httpContext,
                    statusCode: httpContext.Response.StatusCode,
                    detail: authenticateResult.Failure.Message);
    
                var result = new ObjectResult(problemDetails)
                {
                    ContentTypes = new MediaTypeCollection(),
                    StatusCode = problemDetails.Status,
                    DeclaredType = problemDetails.GetType()
                };
    
                await this.Executor.ExecuteAsync(actionContext, result);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      要更改正文或 Http 状态,您可以尝试Context.Response

      这是一个演示代码:

      using Microsoft.AspNetCore.Authentication;
      using Microsoft.Extensions.Logging;
      using Microsoft.Extensions.Options;
      using System.Text.Encodings.Web;
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Http;
      namespace TestIdentity
      {
          public class CustomAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
          {
              public CustomAuthenticationHandler(IOptionsMonitor<TOptions> options
                  , ILoggerFactory logger
                  , UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
              {
      
              }
              protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
              {
                  await Context.Response.WriteAsync("This is why you can't log in");
                  return AuthenticateResult.Fail("This is why you can't log in");
              }
          }
      }
      

      【讨论】:

      • 我确实想到了这样的事情,但是在这样的处理程序中直接写入响应似乎不干净。这是否意味着框架实际上没有提供实现这一点的方法?
      • 当我添加 Context.Response.WriteAsync() 我得到“无法设置状态代码,因为响应已经开始”。此外,StatusCode 显示为 200 而不是 401。不确定上述答案和 Core 3.1 之间是否发生了变化。
      • 这会将状态码更改为 200,因此我们无法获得我们需要获得的真实 http 响应
      • IMO 这是一个不干净的解决方案,即使您想在此处(在处理程序中)执行此操作,您也需要在 HandleForbiddenAsync/HandleChallengeAsync 的覆盖中实现它并修改 Response在调用base 之后(修改StatusCode - 请参见此处source.dot.net/#Microsoft.AspNetCore.Authentication/…)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 2018-12-25
      • 2021-01-05
      • 1970-01-01
      • 2020-02-10
      相关资源
      最近更新 更多