您如何处理在自定义AuthenticationHandler中无法对用户进行身份验证的问题?

时间:2019-11-06 00:16:46

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

我有一个方案,其中应用程序需要通过调用API并发送用户令牌以验证用户身份来对用户进行身份验证。我开始根据以下教程开发自定义身份验证处理程序:

Tutorial 1

Tutorial 2

我有一个非常基本的示例,现在只是简单地使身份验证失败,以确保其有效:

public class SoleAuthenticationHandler : AuthenticationHandler<SoleAuthenticationOptions>
{
    private readonly ISoleApiService _soleApiService;

    public SoleAuthenticationHandler(
        IOptionsMonitor<SoleAuthenticationOptions> options, 
        ILoggerFactory logger, 
        UrlEncoder encoder, 
        ISystemClock clock, ISoleApiService soleApiService) 
        : base(options, logger, encoder, clock)
    {
        _soleApiService = soleApiService;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        return Task.FromResult(AuthenticateResult.Fail("You are not authorized to access this resource."));
    }
}

这按预期工作,用[Authorize]属性修饰的控制器动作将被拦截并抛出401。我的问题如下:

  1. 一旦401发生,我应该如何处理?例如,假设我要将用户重定向到一个友好的页面,该页面显示“您无权请登录”。这是在处理程序中还是其他地方完成的?这里的正确程序是什么?查看AuthenticationHandler的Microsoft文档,有一种名为BuildRedirectUri的方法,但为该方法提供uri并不会真正改变任何内容-页面仍返回401。
  2. 要使其正常工作,现在需要使用[Authorize]属性装饰控制器/动作。有没有办法在全球范围内执行此操作,这样我就不必专门授权每个控制器和/或操作?

1 个答案:

答案 0 :(得分:0)

我们已经/拥有ASP.NET Web表单和MVC 5.x中的customErrors页,用于在发出特定statusCode时自动将用户重定向到指定的错误页:

<customErrors mode="On" defaultRedirect="error">
        <error statusCode="404" redirect="error/notfound" />
        <error statusCode="403" redirect="error/forbidden" />
</customErrors>

在ASP.NET Core中,我们可以通过以下方式模拟这些页面:

首先添加一个新的ErrorController来处理特定的statusCodes(ID在此处),然后返回针对不同条件的自定义视图:

public class ErrorController : Controller
    {
        private readonly ILogger<ErrorController> _logger;

        public ErrorController(ILogger<ErrorController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index(int? id)
        {
            var logBuilder = new StringBuilder();

            var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
            logBuilder.AppendLine($"Error {id} for {Request.Method} {statusCodeReExecuteFeature?.OriginalPath ?? Request.Path.Value}{Request.QueryString.Value}\n");

            var exceptionHandlerFeature = this.HttpContext.Features.Get<IExceptionHandlerFeature>();
            if (exceptionHandlerFeature?.Error != null)
            {
                var exception = exceptionHandlerFeature.Error;
                logBuilder.AppendLine($"<h1>Exception: {exception.Message}</h1>{exception.StackTrace}");
            }

            foreach (var header in Request.Headers)
            {
                var headerValues = string.Join(",", value: header.Value);
                logBuilder.AppendLine($"{header.Key}: {headerValues}");
            }
            _logger.LogError(logBuilder.ToString());

            if (id == null)
            {
                return View("Error");
            }

            switch (id.Value)
            {
                case 401:
                case 403:
                    return View("AccessDenied");
                case 404:
                    return View("NotFound");

                default:
                    return View("Error");
            }
        }
    }

现在是时候将该控制器连接到ASP.NET Core的内置错误处理中间件了:

public void Configure(IApplicationBuilder app)
        {
            if (env.IsDevelopment())
            {
                app.UseDatabaseErrorPage();
                app.UseDeveloperExceptionPage();
            }
            app.UseExceptionHandler("/error/index/500");
            app.UseStatusCodePagesWithReExecute("/error/index/{0}");

关于第二个问题,仅define your filter/Authorize attribute globally