【问题标题】:Custom authentication middleware: InvalidOperationException on failed authentication自定义身份验证中间件:身份验证失败时出现 InvalidOperationException
【发布时间】:2017-12-20 19:05:51
【问题描述】:

我想使用像 Authorization ToggledKey 10079a4c-d27e-4898-915a-968850c756ef 这样的 HTTP 标头来验证对我的 ASP.NET Core 1.0 应用程序的调用

我的想法是我可以发布 API 密钥,人们可以使用它们、撤销它们等。我只想要一个非常简单的密钥 - 我不想进入 OAuth、IdentityServer 或其他任何东西。

我关注了this tutorial,它谈到了如何“伪造”成功的响应以进行测试,我最终得到了以下代码:

public class TestAuthenticationOptions : AuthenticationOptions  
{
    public virtual ClaimsIdentity Identity { get; } = new ClaimsIdentity(new Claim[]
    {
        new Claim(ClaimTypes.Name, Guid.NewGuid().ToString()),
        // Other claims omitted for brevity
    }, "Toggled");

    public TestAuthenticationOptions()
    {
        this.AuthenticationScheme = "ToggledAuthenticationMiddleware";
        this.AutomaticAuthenticate = true;
    }
}

public class TestAuthenticationHandler : AuthenticationHandler<TestAuthenticationOptions>  
{
    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authenticationTicket = new AuthenticationTicket(
                                        new ClaimsPrincipal(Options.Identity),
                                        new AuthenticationProperties(),
                                        this.Options.AuthenticationScheme);

        return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
    }
}

public class TestAuthenticationMiddleware : AuthenticationMiddleware<TestAuthenticationOptions>  
{
    private readonly RequestDelegate next;

    public TestAuthenticationMiddleware(RequestDelegate next, IOptions<TestAuthenticationOptions> options, ILoggerFactory loggerFactory)
        : base(next, options, loggerFactory, System.Text.Encodings.Web.UrlEncoder.Default)
    {
        this.next = next;
    }

    protected override AuthenticationHandler<TestAuthenticationOptions> CreateHandler()
    {
        return new TestAuthenticationHandler();
    }
}

首先,我只是想获得一个硬编码的成功身份验证,这将允许我看到带有User.Identity.Name 的用户和一个硬编码失败的身份验证让我看到401 Unauthorized - 只是为了理解它是如何工作的。

添加到Configure方法中:

app.UseMiddleware<TestAuthenticationMiddleware>(); 
app.UseMvc();

到目前为止,一切都很好,它可以工作 - [Authorize] 属性在控制器上,我在 User.Identity.Name 中获得 GUID

问题:

当我因响应失败而更改时:

return Task.FromResult(AuthenticateResult.Fail("Auth Failed!"));
//return Task.FromResult(AuthenticateResult.Success(authenticationTicket));

我得到的不是 401,而是 500,但有以下例外:

Unknown error responding to request: InvalidOperationException:
System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic
at Microsoft.AspNetCore.Http.Authentication.Internal.DefaultAuthenticationManager.<ChallengeAsync>d__12.MoveNext()

我确实在中间件中看到了身份验证失败:

[Information] ToggledAppServices.TestAuthenticationMiddleware: ToggledAuthenticationMiddleware was not authenticated. Failure message: Auth failed! 

所以我可以看到它确实使用了中间件,身份验证失败,但也看到了 InvalidOperationException 之前:

[Information] Microsoft.AspNetCore.Mvc.ChallengeResult: Executing ChallengeResult with authentication schemes ().

虽然我很困惑。我该怎么做才能使我的中间件成为“唯一”身份验证点,如果它失败,返回 401 而不是尝试做更多事情并因 InvalidOperationException 而失败?

完整日志:

[Debug] Microsoft.AspNetCore.Hosting.Internal.WebHost: Hosting starting 
[Debug] Microsoft.AspNetCore.Hosting.Internal.WebHost: Hosting started 
START RequestId: 52c7358f-e50a-11e7-a5a6-b3632cca0b75 Version: $LATEST
Incoming GET requests to /api/values
[Information] Microsoft.AspNetCore.Hosting.Internal.WebHost: Request starting GET https://www.example.com/api/controller application/json 
[Information] ToggledAppServices.TestAuthenticationMiddleware: ToggledAuthenticationMiddleware was not authenticated. Failure message: Auth failed! 
[Debug] Microsoft.AspNetCore.Routing.Tree.TreeRouter: Request successfully matched the route with name '' and template 'api/values'. 
[Debug] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Executing action ToggledAppServices.Controllers.ValuesController.Get (ToggledAppServices) 
[Information] Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Authorization failed for user: . 
[Warning] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. 
[Information] Microsoft.AspNetCore.Mvc.ChallengeResult: Executing ChallengeResult with authentication schemes (). 
Unknown error responding to request: InvalidOperationException:
System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic
at Microsoft.AspNetCore.Http.Authentication.Internal.DefaultAuthenticationManager.<ChallengeAsync>d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.ChallengeResult.<ExecuteResultAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeResultAsync>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.<ProcessRequest>d__15.MoveNext()
InvalidOperationException:
System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic
at Microsoft.AspNetCore.Http.Authentication.Internal.DefaultAuthenticationManager.<ChallengeAsync>d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.ChallengeResult.<ExecuteResultAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeResultAsync>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.<ProcessRequest>d__15.MoveNext()

[Information] Microsoft.AspNetCore.Hosting.Internal.WebHost: Request finished in 7360.5673ms 0 
Response Base 64 Encoded: False
END RequestId: 52c7358f-e50a-11e7-a5a6-b3632cca0b75

【问题讨论】:

  • 您使用的是 ASP.NET Core 1.x,对吧?您不能使用 2.0 有什么原因吗?身份验证堆栈在 2.0 中变得更容易定制。
  • @poke 它在 AWS Lambda 中运行,因此目前它是唯一受支持的版本。我期待他们提供对 2 的支持 - 我知道它已经宣布但尚未准备好使用。

标签: c# asp.net-core asp.net-core-1.1


【解决方案1】:
System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic
  at Microsoft.AspNetCore.Http.Authentication.Internal.DefaultAuthenticationManager.<ChallengeAsync>d__12.MoveNext()

这表明您的身份验证处理程序的身份验证工作正常,并且由于身份验证失败而正确创建了质询结果。

所以现在,应该运行默认质询身份验证处理程序来处理质询请求。但是,没有配置默认的质询方案,所以失败了。

为了告诉您的TestAuthenticationHandler 也处理质询请求,您应该将TestAuthenticationOptions 类型的AutomaticChallenge 选项设置为true

// handle authenticate requests by default
AutomaticAuthenticate = true;

// handle challenge requests by default
AutomaticChallenge = true;

然后,您的处理程序将针对质询请求运行,由于您不提供自定义实现,the default behavior 只会生成 401 HTTP 响应。

【讨论】:

  • 像魅力一样工作!谢谢 - 我已经花了很多时间了:)
  • 身份验证可能会很痛苦——我希望你有机会尽快升级到 ASP.NET Core 2! :)
猜你喜欢
  • 2022-08-10
  • 1970-01-01
  • 2020-04-01
  • 2022-12-10
  • 2020-10-29
  • 2018-11-02
  • 2021-02-06
  • 2017-11-27
  • 1970-01-01
相关资源
最近更新 更多