Как вернуть 401 вместо 302 в ASP.NET Core?

Я пытаюсь вернуть Core Identity ASP.NET 401, когда пользователь не войдет в систему. Я добавил атрибут [Authorize] к моему методу и вместо того, чтобы возвращать 401, он возвращает 302. Я попробовал массу предложений, но ничего не работает, в том числе services.Configure и app.UseCookieAuthentication установка LoginPath в null или PathString.Empty.

Ответ 1

Как ASP.NET Core 2.x:

services.ConfigureApplicationCookie(options =>
{
    options.Events.OnRedirectToLogin = context =>
    {
        context.Response.StatusCode = 401;    
        return Task.CompletedTask;
    };
});

Ответ 2

Если заголовок запроса содержит X-Requested-With: XMLHttpRequest, код состояния будет 401 вместо 302

private static bool IsAjaxRequest(HttpRequest request)
    {
        return string.Equals(request.Query["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
            string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal);
    }

См. На gitHub: https://github.com/aspnet/Security/blob/5de25bb11cfb2bf60d05ea2be36e80d86b38d18b/src/Microsoft.AspNetCore.Authentication.Cookies/Events/CookieAuthenticationEvenT.

Ответ 3

Для ядра asp.net MVC ИСПОЛЬЗУЙТЕ ЭТО ВМЕСТО

 services.ConfigureApplicationCookie(options =>
        {
            options.LoginPath = new PathString("/Account/Login");
            options.LogoutPath = new PathString("/Account/Logout");

            options.Events.OnRedirectToLogin = context =>
            {
                if (context.Request.Path.StartsWithSegments("/api")
                    && context.Response.StatusCode == StatusCodes.Status200OK)
                {
                    context.Response.Clear();
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    return Task.CompletedTask;
                }
                context.Response.Redirect(context.RedirectUri);
                return Task.CompletedTask;
            };
        });

Ответ 4

services.Configure<IdentityOptions>(options =>
{
   options.Cookies.ApplicationCookie.LoginPath = new PathString("/");
   options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
   {
      OnRedirectToLogin = context =>
      {
         if (context.Request.Path.Value.StartsWith("/api"))
         {
            context.Response.Clear();
            context.Response.StatusCode = 401;
            return Task.FromResult(0);
         }
         context.Response.Redirect(context.RedirectUri);
         return Task.FromResult(0);
      }
   };
});

Источник:

https://www.illucit.com/blog/2016/04/asp-net-5-identity-302-redirect-vs-401-unauthorized-for-api-ajax-requests/

Ответ 5

Хорошо, после того, как я прокопал в тесты ядра ядра asp.net, я наконец нашел рабочее решение. Вы должны добавить к вызову services.AddIdentity

следующее:
services.AddIdentity<ApplicationUser, IdentityRole>(o => {
    o.Cookies.ApplicationCookie.AutomaticChallenge = false;
});

Ответ 6

У меня на ASP.NET Core 2.2.0 работало только это:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(
        options =>
        {
            options.LoginPath = new PathString("/Account/Login");
            options.LogoutPath = new PathString("/Account/Logout");

            options.Events.OnRedirectToLogin = context =>
            {
                if (context.Request.Path.StartsWithSegments("/api")
                    && context.Response.StatusCode == StatusCodes.Status200OK)
                {
                    context.Response.Clear();
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    return Task.CompletedTask;
                }
                context.Response.Redirect(context.RedirectUri);
                return Task.CompletedTask;
            };
        }
    );

Ответ 7

Для ASP.NET Core 3.x (предварительный просмотр), использующего идентификацию с проверкой подлинности с помощью cookie, вот что сделал:

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<IdentityContext>()
    .AddDefaultTokenProviders()
    .AddRoles<IdentityRole>();

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

Это то, что мы видим повсюду в разных вариациях. НО, суть в том, что ConfigureApplicationCookie должно быть указано ПОСЛЕ AddIdentity. Это "грустно" но верно. Этот SO ответ наконец-то принес свет в темноту.

Я целый день ломал голову и пробовал разные варианты:

  • Переопределить атрибут Authorize (больше не нужно переопределять в 3.x)
  • Указание options.Cookie.EventType с файлом cookie (ошибка времени выполнения)
  • options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme (Было сказано, что однонаправленный канал JWT не будет перенаправлять на страницу входа)
  • И, конечно, настройка ApplicationCookie (но до вызова AddIdentity, который не работает.

Это все не сработало. Но с ответом выше я наконец-то вернул 401 UnAuthorized (кстати, это должно быть UnAuthenticated)

Ответ 8

В продолжение я объединил предыдущие ответы в следующие:

1. Startup.cs

services.ConfigureApplicationCookie(options =>
        {
            options.LoginPath = new PathString("/Account/Login");
            options.LogoutPath = new PathString("/Account/Logout");

            options.Events.OnRedirectToAccessDenied = context =>
            {
                if (wlt_AjaxHelpers.IsAjaxRequest(context.Request))
                {
                    context.Response.Clear();
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    return Task.CompletedTask;
                }
                context.Response.Redirect(context.RedirectUri);
                return Task.CompletedTask;
            };
        });

2. Вспомогательный пользовательский класс

public static class wlt_AjaxHelpers
     {

        public static bool IsAjaxRequest( HttpRequest request )
        {

            return string.Equals(request.Query["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
                string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal);
        }

    }