如何在 MVC Core 中被 AuthenticationHandler 重定向后在 httpcontext 中获取状态码
How to get statuscode in httpcontext after being redirected by AuthenticationHandler in MVC Core
我一直在使用 CookieAuthenticationHandler
,但我无法通过访问由具有 Authorize
属性的控制器方法处理的视图进行授权。
CookieAuthenticationHandler
然后将我重定向到 HandleForbiddenAsync
或 HandleChallengeAsync
中的可配置路径(取决于它是身份验证还是授权)。但是,在重定向时我注意到 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。
如果通过在 HandleChallengeAsync
或 HandleForbiddenAsync
方法中简单地执行以下操作就可以消除所有这些...
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);
然后在控制器或视图中我可以简单地检查查询字符串。
我一直在使用 CookieAuthenticationHandler
,但我无法通过访问由具有 Authorize
属性的控制器方法处理的视图进行授权。
CookieAuthenticationHandler
然后将我重定向到 HandleForbiddenAsync
或 HandleChallengeAsync
中的可配置路径(取决于它是身份验证还是授权)。但是,在重定向时我注意到 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。
如果通过在 HandleChallengeAsync
或 HandleForbiddenAsync
方法中简单地执行以下操作就可以消除所有这些...
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);
然后在控制器或视图中我可以简单地检查查询字符串。