如何在 MVC Core 中被 AuthenticationHandler 重定向后在 httpcontext 中获取状态码

How to get statuscode in httpcontext after being redirected by AuthenticationHandler in MVC Core

我一直在使用 CookieAuthenticationHandler,但我无法通过访问由具有 Authorize 属性的控制器方法处理的视图进行授权。

CookieAuthenticationHandler 然后将我重定向到 HandleForbiddenAsyncHandleChallengeAsync 中的可配置路径(取决于它是身份验证还是授权)。但是,在重定向时我注意到 HTTP 状态代码丢失了。

我在重定向到的控制器操作中添加了一个断点,其中状态代码为 200。

我期待不同的状态代码(401 或 403)。


这是 HandleForbiddenAsync 中发生的事情(来自 github):

protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
    var returnUrl = properties.RedirectUri;
    if (string.IsNullOrEmpty(returnUrl))
    {
        returnUrl = OriginalPathBase + Request.Path + Request.QueryString;
    }
    var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl);
    var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(accessDeniedUri));
    await Events.RedirectToAccessDenied(redirectContext);
}

所以我实现了自己的 HandleForbiddenAsync(带有扩展 CookieAuthenticationHandler 的自定义 AuthenticationHandler),并尝试通过添加以下行直接设置状态码:

redirectContext.Response.StatusCode = StatusCodes.Status403Forbidden;

但是当我到达断点时,我仍然看到状态码 200。

我很可能以错误的方式处理事情。我想要完成的是在我的索引视图的控制器中获得不同的状态代码。

有什么想法吗?

我深入研究了源代码并弄清楚了发生了什么。

状态代码在 Events.RedirectToAccessDenied(redirectContext);

中更改

它开始于 CookieAuthenticationEvents (github link)

public virtual Task RedirectToAccessDenied(RedirectContext<CookieAuthenticationOptions> context)
    => OnRedirectToAccessDenied(context);

进入

public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToAccessDenied { get; set; }
    = context =>
{
    if (IsAjaxRequest(context.Request))
    {
        context.Response.Headers["Location"] = context.RedirectUri;
        context.Response.StatusCode = 403;
    }
    else
    {
        context.Response.Redirect(context.RedirectUri);
    }
    return Task.CompletedTask;
};

然后调用抽象 HttpResponse class (github link here) 的 Redirect 方法,在本例中使用 DefaultHttpResponse 实现,即

public override void Redirect(string location, bool permanent)
{
    if (permanent)
    {
        HttpResponseFeature.StatusCode = 301;
    }
    else
    {
        HttpResponseFeature.StatusCode = 302;
    }

    Headers[HeaderNames.Location] = location;
}

所以状态码在这里改变了,重定向状态码被 MVC 获取并变成 200。

如果通过在 HandleChallengeAsyncHandleForbiddenAsync 方法中简单地执行以下操作就可以消除所有这些...

var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri("whatever"));
redirectContext.Response.Headers["Location"] = redirectContext.RedirectUri;
redirectContext.Response.StatusCode = 403;
await Task.CompletedTask;

...然后最终将发送正确的状态码。但是,它现在将由浏览器而不是 MVC 获取,因此即使状态代码现在终于存在,浏览器也会提供默认的浏览器错误页面,仅此而已。


因为我想做的是进入我的重定向页面并显示一条消息,所以我决定只在查询字符串中添加状态代码。这是我的决赛 HandleChallengeAsync:

var unauthorizedUri = Options.LoginPath + QueryString.Create("error", "401");
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(unauthorizedUri));
await Events.RedirectToLogin(redirectContext);

然后在控制器或视图中我可以简单地检查查询字符串。