【问题标题】:How to return 401 instead of 302 in ASP.NET Core?如何在 ASP.NET Core 中返回 401 而不是 302?
【发布时间】:2016-08-06 05:36:24
【问题描述】:

我试图让 ASP.NET Core Identity 在用户未登录时返回 401。我在我的方法中添加了一个 [Authorize] 属性,而不是返回 401,而是返回 302。我已经尝试了大量建议,但似乎没有任何效果,包括 services.Configureapp.UseCookieAuthenticationLoginPath 设置为 nullPathString.Empty

【问题讨论】:

标签: c# asp.net-core asp.net-core-identity


【解决方案1】:

截至 ASP.NET Core 2.x

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

【讨论】:

  • 另外,您对 services.AddIdentity 的调用必须在此之前
  • 我相信 kroatti 的回答应该是公认的。如果您希望 ajax 请求获得 401 但非 ajax 请求被重定向,上述方法将导致问题。根据 Mark Perry 的回答,您可以添加逻辑来处理这个问题,但是这个逻辑已经内置到框架中,用于带有 X-Requested-With: XMLHttpRequest 标头的请求。
  • 我为我的项目编写了三个不同的自定义策略。那么,对于我的所有保单,这是否会返回 401 而不是 302?
  • 这行得通。框架中的一个令人讨厌的功能。如果我们想要重定向,我们会指定它...
  • 对我来说,需要设置 OnRedirectToAccessDenied。
【解决方案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/CookieAuthenticationEvents.cs#L40-L52

【讨论】:

  • 这应该是最直接的答案,不会损害您的代码库。 seesh,为什么我没有早点找到你。
  • 这不是一个很有帮助的答案,因为它要求调用者注入一个专有的标头值。如何修复处理程序代码以实际尊重 Accept: 标头?我的客户端代码(角度)正在发送“接受:应用程序/json”,但返回垃圾 HT​​ML 作为它无法解析的响应。服务器端代码是问题所在。
【解决方案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;
            };
        });

【讨论】:

  • 这个解决方案在 Core 2 中完美运行!谢谢
  • 使用 ASP.NET Core 2.0,您可以使用 return Task.CompletedTask; 而不是 return Task.FromResult<object>(null);
  • 如其他地方所述,请确保这是在致电 services.AddIdentity(…)AddDefaultIdentity(…) 之后进行的
  • 谢谢,这很好用。但是,有没有办法返回 401 和 json(类似于 { authorize: "Failed" })
  • 完美!像魅力一样工作
【解决方案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/

【讨论】:

  • 这正是我所需要的。谢谢!
  • 在 .net core 2.1 上运行良好 - 尽管我确实必须将 StartsWith("/api") 更改为 contains("api") 但在那之后运行良好。我想我现在需要在我的应用程序中做一些事情来检测 401 并在任何 http 请求之后相应地重定向。干杯!
  • 对我来说,需要设置 OnRedirectToAccessDenied。
【解决方案5】:

好吧,在asp.net core unit tests 中挖掘之后,我终于找到了一个可行的解决方案。您必须将以下内容添加到您对services.AddIdentity的调用中

services.AddIdentity<ApplicationUser, IdentityRole>(o => {
    o.Cookies.ApplicationCookie.AutomaticChallenge = false;
});

【讨论】:

  • 这是否仍将用户重定向到登录页面?由于 302,我在点击时会重定向到登录页面的页面获得大量流量。我仍然希望它能够正常工作,但我希望所有机器人都无法调用它,所以他们永远不会调用它再次(401/403)。 302 仍然会告诉他们继续调用它。 (这是针对那些不尊重我特别“禁止”该 url 模式的 robots.txt 文件的人)
  • @ganders 不。如果您希望您的应用程序在 401 上以某种方式运行,您必须自己处理。通常,401 应该包含一个 WWW-Authenticate 标头,描述如何进行身份验证。
  • WWW-Authenticate 标头必须在 401 上提供。If the protected resource request does not include authentication credentials or does not contain an access token that enables access to the protected resource, the resource server MUST include the HTTP "WWW-Authenticate" response header field
  • 这在 .NET Core 3.x(预览版)中似乎(不再存在):/
【解决方案6】:

对于 ASP.NET Core 3.x(预览版)使用 Identity 和 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 必须指定 AFTER AddIdentity。这很“悲伤”,但却是真实的。这个SO answer终于在黑暗中带来了光明。

我已经摸索了一天多,尝试了许多不同的变化:

  • 覆盖 Authorize 属性(不再需要在 3.x 中覆盖)
  • 使用 Cookie 指定 options.Cookie.EventType(运行时错误)
  • options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme(据说JWT承载不会重定向到登录页面)
  • 当然还有配置 ApplicationCookie(但在调用 AddIdentity 之前不起作用。

这一切都没有奏效。但是有了上面的答案,我终于得到了 401 Unauthorized 的返回(顺便说一下应该是Unauthenticated

【讨论】:

  • 无需设置“location”标头——仅对 3xx 状态码有用
  • 嗨@Bernoulli,我正在使用asp.net core 3.1,尝试将类似的代码放在客户端应用程序中,但没有成功。
  • 什么不起作用?它没有编译吗?它抛出异常了吗?什么样的异常/消息?这个答案的重点是声明“事物”的顺序,它解决了 OP 的(和我的)问题。
  • 就我而言,它也不起作用。如果我在 OnRedirectToLogin 或 OnRedirectToAccessDenied 函数中设置断点,它永远不会中断。我不知道如何让它在 .NET Core 3.1 上运行。我的目标是能够根据调用的方法是 API 方法还是网页来重定向到登录页面或返回状态 401。
【解决方案7】:

接下来,我将之前的答案合并为以下内容:

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);
        }

    }

【讨论】:

    【解决方案8】:

    对于 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;
                };
            }
        );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      • 2014-12-19
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      相关资源
      最近更新 更多