C# ASP。Net Core 2.2与cookie身份验证:如何在未授权仅API控制器时避免页面重定向

C# ASP。Net Core 2.2与cookie身份验证:如何在未授权仅API控制器时避免页面重定向,c#,asp.net-mvc,asp.net-core,C#,Asp.net Mvc,Asp.net Core,我的ASP.Net Core2.2web应用程序有一些返回视图的控制器和一些返回json结果的控制器,因为它们只是API控制器 我的网站使用cookie身份验证,声明如下: services .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.AccessDeniedPath = "/Pag

我的ASP.Net Core2.2web应用程序有一些返回
视图的控制器和一些返回json结果的控制器,因为它们只是API控制器

我的网站使用cookie身份验证,声明如下:

services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.AccessDeniedPath = "/Pages/AccessDenied";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Strict;
    });
配置
部分:

// ...
app.UseAuthentication();
app.UseDefaultFiles();
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Pages}/{action=Index}/{id?}");
});
我的
PagesController
只是继承自
Microsoft.AspNetCore.Mvc.Controller
,对于某些页面,我添加了
[Authorize]
属性。当未经授权的用户尝试打开页面时,它会被正确重定向到my public
AccessDenied
页面。一切都很好

但是,我还有一个
ApiController
,它继承自
Microsoft.AspNetCore.Mvc.ControllerBase
,有一个属性
[ApiController]
,还有一个
[Authorize(Roles=“Administrator”)]
。我使用这个控制器与页面中的javascript进行通信。问题是,当非授权用户尝试调用此控制器上的方法时,我不希望它以HTTP 200页面(拒绝访问的
AccessDenied
one)响应,但我希望它只返回HTTP 401 Unauthorized,因为它是API。

如何在保持cookie身份验证的情况下实现如此不同的行为


谢谢

获得此行为的最简单方法是编写一个自定义类,该类派生并重写and

如果您不熟悉
CookieAuthenticationEvents
类,可以从文档中开始。基本上,它是一个为cookie身份验证添加自定义行为的插件

首先,您需要一种识别AJAX请求的方法。最简单的方法是在所有AJAX请求的请求路径中使用公共前缀。例如,您可以决定所有AJAX请求路径都以
/api
开头

您可以编写以下自定义
CookieAuthenticationEvents
类:

public sealed class CustomCookieAuthenticationEvents : CookieAuthenticationEvents 
{
  public override Task RedirectToAccessDenied(
            RedirectContext<CookieAuthenticationOptions> context)
  {
    if (context is null)
    {
      throw new ArgumentNullException(nameof(context));
    }

    if (IsAjaxRequest(context.HttpContext))
    {
      context.Response.StatusCode = StatusCodes.Status403Forbidden;
      return Task.CompletedTask;
    }
    else 
    {
      // non AJAX requests behave as usual
      return base.RedirectToAccessDenied(context);
    }
  }

  public override Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
  {
    if (context is null)
    {
      throw new ArgumentNullException(nameof(context));
    }

    if (IsAjaxRequest(context.HttpContext))
    {
      context.Response.StatusCode = StatusCodes.Status401Unauthorized;
      return Task.CompletedTask;
    }
    else 
    {
      // non AJAX requests behave as usual
      return base.RedirectToLogin(context);
    }
  }

  private static bool IsAjaxRequest(HttpContext httpContext) 
  {
    var requestPath = httpContext.Request.Path;
        return requestPath.StartsWithSegments(new PathString("/api"), StringComparison.OrdinalIgnoreCase);
  }
}

基本上,当一个未经授权的AJAX调用到达一个API控制器时,HTTP响应是一个原始401状态代码,对吗?(而不是302重定向到登录页面或拒绝访问页面)您是否只使用API控制器进行来自前端页面的AJAX调用?这两个问题都是肯定的!
services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.AccessDeniedPath = "/Pages/AccessDenied";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

services.AddSingleton<CustomCookieAuthenticationEvents>();