ASP.NET Core 2.0 отключает автоматическую задачу

После обновления моего проекта ASP.NET Core до версии 2.0 попытки доступа к защищенным конечным точкам больше не возвращают 401, а перенаправляются на (несуществующую) конечную точку, пытаясь дать пользователю возможность аутентификации.

Желаемое поведение для приложения просто для возврата 401. Раньше я устанавливал AutomaticChallenge = false при настройке проверки подлинности, но в соответствии с этой статьей настройка больше не актуальна (на самом деле она больше не существует).

Моя аутентификация настроена следующим образом:

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, когда пользователь не прошел аутентификацию?

Ответ 1

Как указано в некоторых других ответах, больше нет настройки, чтобы отключить автоматический вызов с помощью аутентификации 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

Ответ 2

После некоторых исследований я обнаружил, что мы можем справиться с этой проблемой, хотя ниже:

Мы можем добавить две схемы аутентификации как Identity, так и JWT; и использовать схему идентификации для аутентификации и использовать схему JWT для вызова, JWT не будет перенаправляться на любой логин в процессе вызова.

services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();

services.AddAuthentication((cfg =>
{
    cfg.DefaultScheme = IdentityConstants.ApplicationScheme;
    cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})).AddJwtBearer();

Ответ 3

Согласно эта статья:

В 1.x свойства AutomaticAuthenticate и AutomaticChallenge должны были быть установлены на единой схеме проверки подлинности. Не было хорошего способа обеспечить это.

В 2.0 эти два свойства были удалены как флаги в отдельном экземпляре Аутентификация и переместились в базовый класс AuthenticationOptions. Свойства можно настроить в вызове метода AddAuthentication в ConfigureServices методе Startup.cs

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

В качестве альтернативы используйте перегруженную версию метода AddAuthentication, чтобы установить более одного свойства. В следующем примере перегруженного метода схема по умолчанию установлена ​​в CookieAuthenticationDefaults.AuthenticationScheme. Схема проверки подлинности может быть альтернативно указана в ваших индивидуальных атрибутах [Авторизовать] или политиках авторизации.

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

Определите схему по умолчанию в 2.0, если выполнено одно из следующих условий:

  • Вы хотите, чтобы пользователь автоматически подписался на
  • Вы используете атрибут [Авторизовать] или политики авторизации без указания схемы

Исключением из этого правила является метод AddIdentity. Этот метод добавляет файлы cookie для вас и устанавливает схемы проверки подлинности и вызова по умолчанию в файл cookie приложения IdentityConstants.ApplicationScheme. Кроме того, он устанавливает схему входа по умолчанию для внешнего файла cookie IdentityConstants.ExternalScheme.

Надеюсь, что это поможет вам.

Ответ 4

Совместим с @Serverin, установив OnRedirectToLogin в Cookie приложения, но должен быть выполнен в инструкции следующих служб. AddIdentity в Startup.cs: ConfigureServices:

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

Ответ 5

Это исходный код 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;
};

Вы можете добавить заголовок "X-Requested-With: XMLHttpRequest" в запрос при вызове API от вашего клиента.

Ответ 6

Я не уверен, как сгенерировать ошибку 401, однако, если вы используете:

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

Это позволит вам перенаправить куда-нибудь, когда вызов не удался.

Ответ 7

Другой способ сделать это более удобным для DI/тестирования - это использовать AuthenticationSchemeOptions.EventsType (другой ответ на этот вопрос здесь). Это позволит вам включить другие компоненты в процесс разрешения.

Вот пример, включающий регистрацию и разрешение, которое останавливает перенаправление по умолчанию для входа в систему по неаутентифицированному запросу и вместо этого просто возвращает жесткий 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;
        }
    }
});