【问题标题】:Why does AuthenticationHandler.HandleForbiddenAsync Totally Ignore Code That Runs?为什么 AuthenticationHandler.HandleForbiddenAsync 完全忽略运行的代码?
【发布时间】:2017-01-23 12:25:09
【问题描述】:

感谢您的宝贵时间。 此实现有意不使用内置 cookie、JWT 令牌或任何其他身份验证机制。请不要建议使用任何内置的 Auth 东西来绕过这个问题。这是关于一个完全定制的身份验证/授权实现来诊断和了解正在发生的疯狂行为以及原因。这里的关键词是理解。

public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions> { 

    protected override Task<AuthenticateResult> HandleAuthenticateAsync() {

        // Do stuff and return appropriately after choosing to set the
        // authenticated principle or not.
    }

    protected override Task<bool> HandleUnauthorizedAsync(ChallengeContext context) {

        // This method name suggests that you can choose what happens in the event
        // that Authentication fails. Common sense suggests we should expect a 401
        // which we do see - but something in the internals ignores anything we
        // do here - instead returning a blank page with a 401 result.

        // Note that the event ALWAYS fires and code ALWAYS runs without errors when
        // Authentication fails but the code is entirely ignored (so our redirect 
        // doesnt happen).

        // Adding a specific "ActiveAuthenticationSchemes" proerty to the Controller Action gets the redirect to be honoured.

        // Why offer this override (or even invoke the event) if it will be totally ignored by default???
        // Why REQUIRE that every Authorization attribute must specify an ActiveAuthenticationSchemes
        // property when there is only 1 scheme running? Its madness for large complex applications.

        // This will be ignored by default
        Context.Response.StatusCode = (int)HttpStatusCode.Redirect;
        Context.Response.Redirect("/login?returnUrl=" + Options.UrlEncoder.Encode(httpRequest.Path + httpRequest.QueryString));

         // Return true to stop internals overriding our code? Nope - still ignored.
        return Task.FromResult(true);
    }

    protected override Task<bool> HandleForbiddenAsync(ChallengeContext context) {

        // This method name suggests that you can choose what happens in the event
        // that Authentication succeeds and Authorisation fails. We expect to see
        // a 403 Forbidden here. But again what we see is a blank page and a 401 Unathorized.
        // Something in the internals is ignoring this code and CHANGING what would
        // have been a sensible 403 result into a 401.

        // Like the HandleUnauthorizedAsync method, it is possible to get this
        // method code to be honoured if we explicitly set an ActiveAuthenticationSchemes
        // property on every Authorize attribute.

        // Isnt this again a bonkers requirement for complex applications?

        // This will be ignored in favour of a blank page and a 401
        // We can set either the StatusCode or do the redirect, both will be duly ignored
        Context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
        Context.Response.Redirect("/_403");

        // Return true to stop internals overriding our code? Nope - still ignored.
        return Task.FromResult(true);
    }
}

为了完整起见,启动看起来像这样:

public class Startup {

    public void ConfigureServices(IServiceCollection services) {

        services.AddMvc();
        services.AddDataProtection();
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {

        app.UseCustomAuthenticationMiddleware(

            new CustomAuthenticationOptions() {
                //AuthenticationScheme = "CustomAuthenticationScheme",
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            }
        );

        // -----

        app.UseMvc(routes => {
            routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

我们的中间件有一个简单的实现,它通过以下方式调用处理程序:

protected override AuthenticationHandler<CustomAuthenticationOptions> CreateHandler() {
    return new CustomAuthenticationHandler();
}

那么,是否有某种内置的内部故障保险可以阻止人们连接自定义身份验证,而无需在每个 Action 的每个 Authorize 属性上显式设置 ActiveAuthenticationSchemes(阻止我们这样做)?

我很困惑代码运行时没有错误但随后被忽略。

关于如何自己诊断这个问题的建议和解释一样受欢迎 - 这让我发疯:) !!!

* 编辑/更新

可能的线索。似乎 [Authorize] 属性也有一些令人发指的奇怪行为。当在 Action 级别应用“ActiveAuthenticationSchemes”时,事情开始按您对 Unauthorized 和 Forbidden 结果的预期工作。不过,将该属性移至控制器级别,它仅对未授权有效。被禁止的处理程序代码再次被忽略(导致空白页和 401 而不是编码重定向)。这里内部的某些东西正在践踏合理甚至编码的结果。不是吗? :) ...查看 MVC 源代码,看起来 MVC 可能会强制执行 401 而不是质询结果。我希望那不是真的……

* 编辑 2

这似乎是,至少部分是真的。我创建了一个只运行 MVC 的新项目(没有额外的身份验证或授权),并且无论您是否经过身份验证,MVC 都会返回 401 Unauthorized。感觉这应该是 403 Forbidden if Authorized 但权限不足。很明显,身份验证和 MVC 之间没有连接,导致代码被忽略并且行为被 MVC 401 结果覆盖。也许……还在调查……

【问题讨论】:

  • 我认为这是因为您在某处有一个授权中间件,它可以捕获并处理它,如果您的 .UseIdentity(...) 方法中有 .UseIdentity(...) ,则可能是 Identity。您是否也使用完全相同的授权方案?
  • 我没有使用身份、Cookie、令牌或任何其他类型的内置身份验证或授权工具。我故意试图理解似乎有点奇怪的默认行为。我正在使用 MVC,所以也许这可能会返回 401 而不是挑战结果或其他什么?
  • 您是否有其他已注册的政策,例如options.AddPolicy("", builder =&gt; builder.RequireAuthenticatedUser());
  • 不,没有政策 - 所有代码都已发布。看起来可能是 MVC 我会更新帖子。

标签: asp.net-core authorization


【解决方案1】:

要解决它,请改用 HandleChallengeAsync 方法:

受保护的覆盖任务 HandleChallengeAsync(AuthenticationProperties 属性){ 字符串重定向网址; if (ReadConfig.LoginURL_Exists && Request.AcceptHTML ()) { redirectURL = TextHelper.AppendVarValueToHTTPURL (ReadConfig.LoginURL, GeneralConstants.RETURN_URL, Request.GetDisplayUrl()); Response.Redirect(重定向URL); 返回Task.CompletedTask; } Response.StatusCode = (int) HttpStatusCode.Unauthorized; 返回Task.CompletedTask; }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 2023-03-17
    • 1970-01-01
    • 2014-10-05
    • 2019-04-16
    • 2022-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多