【问题标题】:ASP.NET Core 2.0 disable automatic challengeASP.NET Core 2.0 禁用自动质询
【发布时间】:2018-02-03 07:30:54
【问题描述】:

将我的 ASP.NET Core 项目升级到 2.0 后,尝试访问受保护的端点不再返回 401,而是重定向到(不存在的)端点以尝试让用户进行身份验证。

所需的行为是应用程序简单地返回 401。以前我会在配置身份验证时设置AutomaticChallenge = false,但根据this article,该设置不再相关(实际上它不再存在)。

我的身份验证是这样配置的:

Startup.cs.ConfigureServices():

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(o =>
                {
                    o.Cookie.Name = options.CookieName;
                    o.Cookie.Domain = options.CookieDomain;
                    o.SlidingExpiration = true;
                    o.ExpireTimeSpan = options.CookieLifetime;
                    o.TicketDataFormat = ticketFormat;
                    o.CookieManager = new CustomChunkingCookieManager();
                });

配置():

app.UseAuthentication();

如何禁用自动质询,以便应用程序在用户未通过身份验证时返回 401?

【问题讨论】:

  • 您是如何让您的用户在 401 之后登录的?
  • 我为(临时)解决这个问题所做的是使用我的自定义 [Auth] 属性,我返回的是 400 而不是 401。
  • 您能说说这个属性的作用吗?
  • 参考:Setting default authentication schemes 讨论了对AutomaticChallengeAutomaticAuthenticate 的更改

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


【解决方案1】:

我不确定如何生成 401 错误,但是如果您使用:

o.AccessDeniedPath = "{path to invalid}";

这将允许您在挑战失败时重定向到某个地方。

【讨论】:

    【解决方案2】:

    this文章:

    在 1.x 中,AutomaticAuthenticateAutomaticChallenge 属性旨在设置在单一身份验证方案上。没有很好的方法来强制执行此操作。

    在 2.0 中,这两个属性已作为单独的 AuthenticationOptions 实例上的标志被删除,并已移至基本 AuthenticationOptions 类中。可以在 Startup.cs 的 ConfigureServices 方法中的 AddAuthentication 方法调用中配置属性

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
    

    或者,使用 AddAuthentication 方法的重载版本来设置多个属性。在以下重载方法示例中,默认方案设置为 CookieAuthenticationDefaults.AuthenticationScheme。身份验证方案也可以在您的个人 [Authorize] 属性或授权策略中指定。

    services.AddAuthentication(options => {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    });
    

    如果以下条件之一为真,则在 2.0 中定义默认方案:

    • 您希望用户自动登录
    • 您使用 [Authorize] 属性或授权策略但未指定 方案

    此规则的一个例外是 AddIdentity 方法。此方法为您添加 cookie 并将默认身份验证和质询方案设置为应用程序 cookie IdentityConstants.ApplicationScheme。此外,它将默认登录方案设置为外部 cookie IdentityConstants.ExternalScheme

    希望对您有所帮助。

    【讨论】:

    • 感谢您的回复,但恐怕它并不能解决我的问题。如果我没有指定 DefaultChallengeScheme,应用程序会抛出错误:“InvalidOperationException:未指定 authenticationScheme,并且未找到 DefaultChallengeScheme。”
    【解决方案3】:

    经过一番研究,我发现我们可以通过以下方法解决这个问题:

    我们可以添加两个身份验证方案,分别是 Identity 和 JWT;并使用 Identity 方案进行身份验证并使用 JWT 模式进行质询,JWT 在质询时不会重定向到任何登录路由。

    services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
    
    services.AddAuthentication((cfg =>
    {
        cfg.DefaultScheme = IdentityConstants.ApplicationScheme;
        cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })).AddJwtBearer();
    

    【讨论】:

    • 这是一个有效的答案。以前在 ASP.NET Core 1.1 中,我使用完全相同的方法,升级到 2.0 后,我在配置 JwtBearer 身份验证时遇到了一些麻烦,但按照您的代码解决了这个问题。现在一切都像魅力一样运作。
    • 请记住,顺序很重要。 AddAuthentication 在 AddIdentity 之前对我不起作用。此答案中的顺序是正确的。
    • ASP.NET Core 文档页面中的其他详细信息:Setting default authentication schemes
    • 我必须登录才能给这个答案一个赞成票,几个月来我一直在努力实现这个目标!这在 2.x 中完美运行!
    • 我遇到了非常相似的事情。对于我的特殊情况,我还必须设置 DefaultSignInSchemeDefaultAuthenticateScheme 以使用 JwtBearerDefaults.AuthenticationScheme。感谢您为我指明正确的方向。
    【解决方案4】:

    正如其他一些答案所指出的,不再有通过 cookie 身份验证关闭自动质询的设置。解决方法是覆盖OnRedirectToLogin:

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
             {                 
                 options.Events.OnRedirectToLogin = context =>
                 {
                     context.Response.Headers["Location"] = context.RedirectUri;
                     context.Response.StatusCode = 401;
                     return Task.CompletedTask;
                 };
             });
    

    这在未来可能会改变:https://github.com/aspnet/Security/issues/1394

    【讨论】:

      【解决方案5】:

      与@Serverin 类似,设置应用程序Cookie 的OnRedirectToLogin 有效,但必须在Startup.cs:ConfigureServices 中的services.AddIdentity 之后的语句中完成:

      services.ConfigureApplicationCookie(options => {
        options.Events.OnRedirectToLogin = context => {
          context.Response.Headers["Location"] = context.RedirectUri;
          context.Response.StatusCode = 401;
          return Task.CompletedTask;
        };
      });
      

      【讨论】:

      • 我的男人!最后!我在这个主题上尝试了很多变化,这成功了!在调用services.AddIdentity 之后进行配置。这种声明顺序有多么奇怪:/微软?!
      • 这应该是我可以接受的答案(至少对于 .NET Core 3.x(预览版)),我可以让它工作。
      • 无需设置“位置”标头——仅对 3xx 状态码有用
      • @Andy location header可以帮助js/angular代码重定向登录。因为当 isp 在不同的域中时,来自 ajax 调用的自动登录 (302) 将导致 CORS 错误。因此需要 401 响应。
      【解决方案6】:

      这是 CookieAuthenticationEvents.OnRedirectToLogin 的源代码:

      public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
      {
          if (IsAjaxRequest(context.Request))
          {
              context.Response.Headers["Location"] = context.RedirectUri;
              context.Response.StatusCode = 401;
          }
          else
          {
              context.Response.Redirect(context.RedirectUri);
          }
          return Task.CompletedTask;
      };
      

      您可以在从客户端进行 API 调用时将“X-Requested-With: XMLHttpRequest”标头添加到请求中。

      【讨论】:

      【解决方案7】:

      另一种对 DI/测试更友好的方法是使用AuthenticationSchemeOptions.EventsType(另一个答案points at it here)。这将允许您将其他组件拉入解析过程。

      这是一个包含注册和解析的示例,它会停止默认重定向以在未经身份验证的请求上登录,而只是返回一个硬401。它还有一个插槽,用于可能需要了解未经身份验证的请求的任何其他依赖项。

      Startup.cs:

      services
          .AddAuthentication("MyAuthScheme")
          .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
          {
              options.EventsType = typeof(MyEventsWrapper);
          };
      
      ...
      
      services.AddTransient<MyEventsWrapper>();
      services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
      

      然后,在MyEventsWrapper.cs

      public class MyEventsWrapper : CookieAuthenticationEvents
      {
          private readonly IHttpContextAccessor _accessor;
          private readonly IDependency _otherDependency;
      
          public MyEventsWrapper(IHttpContextAccessor accessor,
                                 IDependency otherDependency)
          {
              _accessor = accessor;
              _otherDependency = otherDependency;
          }
      
          public override async Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
          {
              context.Response.Headers.Remove("Location");
              context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
              await _otherDependency.Cleanup(_accessor.HttpContext);
          }
      }
      

      【讨论】:

        【解决方案8】:

        我发现在大多数情况下解决方案是覆盖

        OnRedirectToLogin

        但在我的应用程序中,我使用了多个身份验证策略,并且覆盖 OnRedirectToLogin 对我不起作用。我的解决方案是添加一个简单的中间件来重定向传入的请求。

        app.Use(async (HttpContext context, Func<Task> next) => {
            await next.Invoke(); //execute the request pipeline
        
            if (context.Response.StatusCode == StatusCodes.Status302Found && context.Response.Headers.TryGetValue("Location", out var redirect)) {
                var v = redirect.ToString();
                if (v.StartsWith($"{context.Request.Scheme}://{context.Request.Host}/Account/Login")) {
                    context.Response.Headers["Location"] = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
                    context.Response.StatusCode = 401;
                }
            }
        });
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-08-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-20
          • 1970-01-01
          • 2018-06-23
          • 2018-03-12
          相关资源
          最近更新 更多