【问题标题】:ASP.NET core 2.2: what is the expected behaviour of ChallengeResult when there are multiple authentication schemes configured?ASP.NET core 2.2:当配置了多个身份验证方案时,ChallengeResult 的预期行为是什么?
【发布时间】:2019-04-10 11:52:46
【问题描述】:

我们正在尝试了解在注册了多个身份验证方案时对 ChallengeResult 的预期处理方式。

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

This documentation 似乎证实了我们想要实现的目标完全可以使用 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 处理方面可能出现的“不兼容”,因为它们被 both 调用?

我们认为双方都有机会对请求进行身份验证很好,但我们想知道是否有可能决定由哪一个来处理身份验证挑战。

感谢您的帮助!

【问题讨论】:

    标签: c# authentication asp.net-core .net-core authorization


    【解决方案1】:

    您不应在 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 身份验证。

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

    【讨论】:

    • 按照您的建议进行操作是否可以让 both 身份验证处理程序有机会执行身份验证,但强制(基于适当的条件)仅处理身份验证质询一个?如果可能的话,我们应该根据我们收到的调用类型(如果 angularjs 则 cookie 否则自定义处理程序)为身份验证质询选择正确的处理程序。对吗?
    • 出于好奇,您想支持哪两个方案?
    • 该应用程序当前是用 .NET 4.7.1 编写的,它是一个 ASP.NET MVC 5 应用程序。我们希望将其迁移到 ASP.NET core 2.2。所有的身份验证机制都是自定义的,我们基本上有一个自定义的身份服务器实现。迁移的顶级计划是为 angularjs 客户端应用程序使用 cookie 方案,并为第三方应用程序使用基于 Authorize HTTP 请求标头的自定义方案(使用我们的基础设施的项目执行的客户端到服务器调用的服务器到服务器调用)。
    • 目前我们有一些 ASP.NET web api 2 控制器供 angularjs 客户端和第三方集成商使用。当前(自定义)身份验证机制执行多次尝试来验证请求,它基本上检查 cookie 和 Authorize 标头。如果它找到它们中的任何一个,它就会为请求用户主体设置一个身份,并且该请求已通过身份验证。我们正试图弄清楚如何将此实现移植到 ASP.NET 核心。
    • 除了我们特定的生产场景,我想了解ASP.NET核心的身份验证框架是如何设计来处理这种场景的挑战(我的意思是,最佳实践是什么?使用提供的 api 进行身份验证的预期方式是什么?)。
    猜你喜欢
    • 2020-10-29
    • 2017-02-09
    • 1970-01-01
    • 2019-06-13
    • 2019-09-06
    • 2019-08-31
    • 2018-02-23
    • 1970-01-01
    • 2017-10-03
    相关资源
    最近更新 更多