【问题标题】:How to remove the redirect from an ASP.NET Core webapi and return HTTP 401?如何从 ASP.NET Core webapi 中删除重定向并返回 HTTP 401?
【发布时间】:2015-09-30 09:53:31
【问题描述】:

根据this question 上的回答,我默认为所有内容添加了授权,使用以下代码:

public void ConfigureServices(IServiceCollection aServices)
{
  aServices.AddMvc(options =>
  {
     var lBuilder = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();

     var lFilter = new AuthorizeFilter(lBuilder.Build());
     options.Filters.Add(lFilter);
   });

   aServices.AddMvc();
}

public void Configure(IApplicationBuilder aApp, IHostingEnvironment aEnv, ILoggerFactory aLoggerFactory)
{
  aApp.UseCookieAuthentication(options =>
  {
    options.AuthenticationScheme = "Cookies";
    options.AutomaticAuthentication = true;
  });
}

但是,当有人试图访问未经授权的内容时,它会返回一个(似乎是默认的)重定向 URL (http://foo.bar/Account/Login?ReturnUrl=%2Fapi%2Ffoobar%2F)。

我希望它只返回 HTTP 401,而不是重定向。

如何在 ASP.NET 5 中为 WebAPI 执行此操作?

【问题讨论】:

  • 请在此处包含您的代码,而不是链接到其他问题。
  • 谢谢,我添加了代码。
  • 嗨,这是所有代码吗?您使用什么身份验证?令牌、cookie、外部?
  • 您好@Geerten,由于该方法不再有效,您能否更改已接受的答案?

标签: asp.net asp.net-web-api authorization asp.net-core


【解决方案1】:

我在 Angular2 + ASP.NET Core 应用程序中遇到过这个问题。我设法通过以下方式修复它:

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

如果这对您不起作用,您可以尝试使用以下方法:

services.AddIdentity<ApplicationUser, IdentityRole>(config =>   {
    // ...
    config.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents
    {
       OnRedirectToLogin = ctx =>
       {
           if (ctx.Request.Path.StartsWithSegments("/api")) 
           {
               ctx.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
               // added for .NET Core 1.0.1 and above (thanks to @Sean for the update)
               ctx.Response.WriteAsync("{\"error\": " + ctx.Response.StatusCode + "}");
           }
           else
           {
               ctx.Response.Redirect(ctx.RedirectUri);
           }
           return Task.FromResult(0);
       }
    };
    // ...
}

Asp.Net Core 2.0 更新

Cookie 选项现在按以下方式配置:

services.ConfigureApplicationCookie(config =>
            {
                config.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToLogin = ctx => {
                        if (ctx.Request.Path.StartsWithSegments("/api"))
                        {
                            ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        }
                        else {
                            ctx.Response.Redirect(ctx.RedirectUri);
                        }
                        return Task.FromResult(0);
                    }
                };
            });

【讨论】:

  • 问题/澄清 -- 为什么我们要在更改之前检查 HTTP 200?如果重定向发生,我们不应该采取行动吗?提前致谢。
  • 相应地更新了答案。
  • 不错的答案,为我指明了正确的方向。您可能还希望使用 OnRedirectToAccessDenied 对经过身份验证(登录)但未授权的用户使用 HttpStatusCode.Forbidden 用于 api 情况,并使用不带 ReturnString 的重定向用于 View 用户。
  • 这是 ASP.NET MVC Core 和 Angular 2 应用程序的正确答案。谢谢!!!!给我开掩体。注意:当通过 cookie 使用 JWT 令牌身份验证时,这是必需的 - 尤其是从 JWT 不记名身份验证切换时。
  • 似乎不适用于 .NET core 3.0(预览版):/
【解决方案2】:

通过您被重定向到的 url,我假设您正在使用 cookie 身份验证。

应该通过其中一位用户将CookieAuthenticationOptionsLoginPath 属性设置为null 或空described 来获得所需的结果。

app.UseCookieAuthentication(options =>
        {
            options.LoginPath = "";
        });

当时它可能还在工作,但现在不再工作了(因为 this 更改)。

我为此提交了bug on GitHub

一旦解决,我会更新答案。

【讨论】:

  • 确实我使用cookie认证,所以我添加了CookieAuthentication代码。感谢您添加了错误请求,它在这里也不起作用!
  • 感谢您提出问题。阅读讨论非常有帮助。
  • 我同意那个 github 错误线程是一个非常好的阅读。总结一下,使用 cookie 对 web api 来说确实不是很好。
  • 不再有效。 config.Cookies.ApplicationCookie.Events 答案是现在有效的答案
【解决方案3】:

设置 LoginPath = "" 或 null 不再适用于版本 1.1.0.0。所以这就是我所做的:

app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            ExpireTimeSpan = TimeSpan.FromDays(150),
            AuthenticationScheme = options.Cookies.ApplicationCookie.AuthenticationScheme,
            Events = new CookieAuthenticationEvents
            {
                OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync,
                OnRedirectToLogin = async (context) => context.Response.StatusCode = 401,
                OnRedirectToAccessDenied = async (context) => context.Response.StatusCode = 403
            },
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
        });

【讨论】:

  • 请注意——您还应该拥有await Task.FromResult(0);,以便观察异步方法中抛出的任何异常。例如OnRedirectToLogin = async (context) =&gt; { context.Response.StatusCode = 401; await Task.FromResult(0); },
【解决方案4】:

我遇到了类似的问题。 我通过手动添加服务解决了这个问题。

ConfigureServices 方法:

    services.AddTransient<IUserStore<User>, UserStore<User, IdentityRole, ApplicationDbContext>>();   
    services.AddTransient<IPasswordHasher<User>, PasswordHasher<User>>();
    services.AddTransient<IUserValidator<User>, UserValidator<User>>();
    services.AddTransient<ILookupNormalizer, UpperInvariantLookupNormalizer>();
    services.AddTransient<IPasswordValidator<User>, PasswordValidator<User>>();
    services.AddTransient<IdentityErrorDescriber, IdentityErrorDescriber>();
    services.AddTransient<ILogger<UserManager<User>>, Logger<UserManager<User>>>();
    services.AddTransient<UserManager<User>>();

    services.AddMvcCore()
    .AddJsonFormatters()
    .AddAuthorization();


    services.AddCors(options=> {
    options.AddPolicy("AllowAllHeaders", (builder) => {
        builder.WithOrigins("*").AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("WWW-Authenticate"); ;
    });
});


    services.AddAuthentication(options=> {
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.ApiName = "api1";
        options.ApiSecret = "secret";
    });

配置方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("AllowAllHeaders");
    app.UseAuthentication();
    app.UseMvc();

}

我正在使用 aspnet core 2.0、IdentityServer 4 和 aspnet identity。

【讨论】:

  • 谢谢你,你的帖子帮助了我。
  • 就我而言,问题是我使用的是services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)。我以为它只会覆盖所有参数。显然我错了。您需要用JwtBearerDefaults.AuthenticationScheme; 覆盖options.DefaultChallengeScheme,然后它才能工作。在 ASP.NET Core 预览版 3 上测试
【解决方案5】:

请注意,只有在您想使用自己的身份验证机制(例如绕过 Identity 提供程序,而我们大多数人都不是这种情况)时,您才应该使用 CookieAuthentication

默认Identity提供者在后台使用CookieAuthenticationOptions,你可以像下面这样配置。

services.AddIdentity<ApplicationUser, IdentityRole>(o =>
 {
     o.Password.RequireDigit = false;
     o.Password.RequireUppercase = false;
     o.Password.RequireLowercase = false;
     o.Password.RequireNonAlphanumeric = false;
     o.User.RequireUniqueEmail = true;

     o.Cookies.ApplicationCookie.LoginPath = null; // <-----
 })
 .AddEntityFrameworkStores<ApplicationDbContext>()
 .AddDefaultTokenProviders(); 

在版本1.0.0中测试

【讨论】:

    【解决方案6】:

    如果有帮助,下面是我的答案 - 使用 dotnet 1.0.1

    它基于 Darkseal 的回答,除了我必须添加行 ctx.Response.WriteAsync() 以停止重定向到默认 401 URL(帐户/登录)

            // Adds identity to the serviceCollection, so the applicationBuilder can UseIdentity
            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {                
                //note: this has no effect - 401 still redirects to /Account/Login!
                //options.Cookies.ApplicationCookie.LoginPath = null;
    
                options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToLogin = ctx =>
                    {
                        //for WebApi: prevent aspnet core redirecting to 'Account/Login' on a 401:
                        if (ctx.Request.Path.StartsWithSegments("/api"))
                        {
                            ctx.RedirectUri = null;
                            ctx.Response.WriteAsync("{\"error\": " + ctx.Response.StatusCode + "}");
                        }
                        else
                        {
                            ctx.Response.Redirect(ctx.RedirectUri);
                        }
                        return Task.FromResult(0);
                    }
                };
            })
                .AddDefaultTokenProviders();
        }
    

    【讨论】:

      【解决方案7】:

      Startup 中使用此代码:

      services.ConfigureApplicationCookie(options =>
                  {
                      options.LoginPath = $"/Account/Login";
                      options.LogoutPath = $"/Account/Logout";
                      options.AccessDeniedPath = $"/Account/AccessDenied";
                      options.Events = new CookieAuthenticationEvents()
                      {
                          OnRedirectToLogin = (ctx) =>
                          {
                              if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
                                  ctx.Response.StatusCode = 401;
                              return Task.CompletedTask;
                          },
                          OnRedirectToAccessDenied = (ctx) =>
                          {
                              if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
                                  ctx.Response.StatusCode = 403;
                              return Task.CompletedTask;
                          }
                      };
                  });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-13
        • 2018-11-05
        • 2017-04-09
        • 2015-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-14
        相关资源
        最近更新 更多