繁体   English   中英

ASP.NET core 2.2:当配置了多个身份验证方案时,ChallengeResult 的预期行为是什么?

[英]ASP.NET core 2.2: what is the expected behaviour of ChallengeResult when there are multiple authentication schemes configured?

我们试图了解当注册多个身份验证方案时,对 ChallengeResult 的预期处理是什么。

我们需要处理这样的场景,因为我们有一个 ASP.NET 核心 2.2 应用程序公开了一些操作方法(我们使用 MVC 中间件),必须由依赖 cookie 身份验证的 angularjs SPA 和一些使用身份验证的第三方应用程序使用基于 Authorization HTTP 请求头的机制。 请注意,涉及的操作方法对于两个用户是相同的,这意味着他们每个人都必须允许使用 cookie 和基于授权 HTTP 请求标头的自定义方案进行身份验证。 我们知道这可能不是最佳设计,但我们无法修改整体架构。

该文档似乎证实了使用 ASP.NET core 2.2 完全可以实现我们想要实现的目标。 不幸的是,UI 应用程序使用的 cookie 身份验证和第三方使用的自定义身份验证必须在身份验证质询的情况下表现不同,并且它们的预期行为彼此不兼容:UI 应用程序应将用户重定向到登录表单,而第三方应用程序需要原始 401 状态代码响应。 上面链接的文档没有提供对 ChallengeResult 处理的清晰解释,因此我们决定尝试一个测试应用程序。

我们创建了两个假身份验证处理程序:

public class FooAuthenticationHandler : IAuthenticationHandler
  {
    private HttpContext _context;

    public Task<AuthenticateResult> AuthenticateAsync()
    {
      return Task.FromResult(AuthenticateResult.Fail("Foo failed"));
    }

    public Task ChallengeAsync(AuthenticationProperties properties)
    {
      _context.Response.StatusCode = StatusCodes.Status403Forbidden;
      return Task.CompletedTask;
    }

    public Task ForbidAsync(AuthenticationProperties properties)
    {
      return Task.CompletedTask;
    }

    public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
    {
      _context = context;
      return Task.CompletedTask;
    }
  }
public class BarAuthenticationHandler : IAuthenticationHandler
  {
    private HttpContext _context;

    public Task<AuthenticateResult> AuthenticateAsync()
    {
      return Task.FromResult(AuthenticateResult.Fail("Bar failed"));
    }

    public Task ChallengeAsync(AuthenticationProperties properties)
    {
      _context.Response.StatusCode = StatusCodes.Status500InternalServerError;
      return Task.CompletedTask;
    }

    public Task ForbidAsync(AuthenticationProperties properties)
    {
      return Task.CompletedTask;
    }

    public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
    {
      _context = context;
      return Task.CompletedTask;
    }
  }

我们在 ConfigureServices 方法中注册了身份验证模式,如下所示:

public void ConfigureServices(IServiceCollection services)
    {
      services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

      services.AddAuthentication(options => 
      {
        options.DefaultChallengeScheme = "Bar";
        options.AddScheme<FooAuthenticationHandler>("Foo", "Foo scheme");
        options.AddScheme<BarAuthenticationHandler>("Bar", "Bar scheme");
      });
    }

这是我们的中间件管道:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }
      else
      {
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
      }

      app.UseHttpsRedirection();

      app.UseAuthentication();

      app.UseMvc();
    }

最后,我们创建了一个控制器,其动作方法需要身份验证:

[Route("api/[controller]")]
  [ApiController]
  public class ValuesController : ControllerBase
  {
    // GET api/values/5
    [HttpGet("{id}")]
    [Authorize(AuthenticationSchemes = "Foo,Bar")]
    public ActionResult<string> Get(int id)
    {
      return "value";
    }
  }

我们注意到:

  • FooAuthenticationHandlerBarAuthenticationHandler都被调用来处理 ChallengeResult
  • 顺序是FooAuthenticationHandlerBarAuthenticationHandler并取决于Authorize属性(如果换成内部的认证方案Authorize属性,那么BarAuthenticationHandler首次调用)
  • 调用者获得原始 500 状态代码响应,但这仅取决于调用授权处理程序的顺序
  • 调用options.DefaultChallengeScheme = "Bar"; 当且仅当[Authorize]属性中设置属性AuthenticationSchemes时才重要。 如果这样做,只有BarAuthenticationHandler被称为和FooAuthenticationHandler从来没有得到一个机会来验证请求或处理一个认证挑战。

因此,问题基本上是:当您遇到这种情况时,您希望如何处理有关 ChallengeResult 处理的不同身份验证方案的可能“不兼容”,因为它们被调用了?

在我们看来,两者都有机会对请求进行身份验证是可以的,但我们想知道是否有可能决定哪一个应该处理身份验证挑战。

感谢您的帮助!

您不应在 Authorize 属性上指定方案。 相反,指定一个方案作为默认方案,并设置一个前向选择器。

选择器的实现取决于您的情况,但通常您可以以某种方式找出请求中使用的方案。

例如,以下是 OpenID Connect 方案设置的示例。

o.ForwardDefaultSelector = ctx =>
{
    // If the current request is for this app's API
    // use JWT Bearer authentication instead
    return ctx.Request.Path.StartsWithSegments("/api")
        ? JwtBearerDefaults.AuthenticationScheme
        : null;
};

因此,如果路由以 /api 开头,那么它所做的就是将挑战(以及一切)转发给 JWT 处理程序。 您可以在那里进行任何类型的检查,标题等。

因此,在这种情况下,OpenID Connect 和 Cookies 被设置为所有内容的默认值,但如果接收到将要发送到 API 的调用,请使用 JWT 身份验证。

此处的示例转发了您可以通过身份验证执行的所有“操作”(挑战、禁止等)。 您还可以为挑战等设置前向选择器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM