如何在未经授权的情况下重定向到我的登录屏幕,同时还在MVC中返回401状态代码?

dev*_*obf 8 asp.net-mvc

在我们的MVC网站上,我们使用该Authorize属性保护我们的端点,但是当我发生这种情况时,我想将用户重定向到登录屏幕.我的第一个解决方案是对重定向Application_EndRequestGlobal.asax.cs:

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        if (Response.StatusCode == (int) HttpStatusCode.Unauthorized)
        {
            Response.ClearContent();
            Response.Redirect("/Account/Login");
        }
    }
Run Code Online (Sandbox Code Playgroud)

这个问题是我们的一些视图是由AJAX加载的,在这种情况下我们不希望在动态加载的div中显示登录屏幕(这例如在注销后按下后退按钮时会发生).如果请求仍返回登录屏幕,但使用401 Unauthorized状态代码以便我们可以检测到此事件,那会更好.显然这不适用于这种方法,因为重定向依赖于302状态代码.

相反,我宁愿Application_EndRequest用401状态代码"插入"登录屏幕内容.所以,而不是Response.Redirect,我试过这个:

            ...
            Response.ClearContent();
            Server.TransferRequest("~/Account/Login");
            Response.StatusCode = (int) HttpStatusCode.Unauthorized;
            ...
Run Code Online (Sandbox Code Playgroud)

重定向工作得很好,但因为这会执行一个新请求,我无法以这种方式设置状态代码,我得到200.仍然没有检测到未经授权的请求.

我也尝试过:

            ...
            Response.ClearContent();
            Server.Transfer("~/Account/Login");
            Response.StatusCode = (int) HttpStatusCode.Unauthorized;
            ...
Run Code Online (Sandbox Code Playgroud)

但我得到一个异常"执行/帐户/登录的子请求时出错".

我也尝试过:

            ...
            Response.ClearContent();
            HttpContext.Current.RewritePath("~/Account/Login");
            Response.StatusCode = (int) HttpStatusCode.Unauthorized;
            Response.End();
            ...
Run Code Online (Sandbox Code Playgroud)

但这引发了异常"线程被中止".我不确定Response.End()是否必要,但没有它,我只是得到一个标准的IIS 401错误.

如何返回登录屏幕的内容,但是有401状态代码?

Dav*_*ich 8

发送401和ViewObject或RedirectObject并不是MVC框架的工作方式: - 一些帖子巧妙地显示了返回带有View授权过滤器的401.

我指定了MVC框架,但这甚至不是使用HTTP协议的首选方式 .

  • MVC应用程序中重定向时,您已创建了RedirectResult
  • 当您的MVC应用程序通过调用Controller的View()方法返回时,将创建一个ViewResult

无论重定向还是视图最终都到达客户端浏览器,

  • HTTP Return Code200 - 成功将返回

要么

  • HTTP Return Code302 - 重定向将被退回

这种约定的原因是,服务器正在"处理"重定向.如果用户未获得授权,则服务器会将用户重定向到登录页面.

同样地,在AJAX或异步HTTP请求中,只要返回HTML视图,HTTP请求就被认为是成功的.异步请求不返回重定向(302).


向客户端发送错误代码:

如果您想将401 未经授权的人返回给客户端,这通常意味着您希望客户端通过导航到"登录"或"错误视图"来"处理"未经授权的请求.

$.ajax({
    url: loginURL,
    data: $("#form").serialize(),
    type: "POST",
    dataType: "html"
})
.success(function (result, status) {
            /*additional logic*/
})
.error(function (xhr, status) {
    if (xhr.status == 401)
    {
        window.location = unauthorizedUrl; //redirect to unauthorized url
    }
});
Run Code Online (Sandbox Code Playgroud)

如果要重定向到登录页面并显示错误该怎么办?

如果要重定向到登录页面并显示错误,那么您的问题最有意义

登录尝试失败

然后,惯例是将您的错误消息添加到ViewbagModelState错误

如:

        var user = await signInManager.UserManager.FindByNameAsync(model.UserName);

                ModelState.AddModelError("", "Invalid username or password.");
                return View(model);
Run Code Online (Sandbox Code Playgroud)

在您的视图中,要有代码来查找错误:

A ValidationSummary会拾取模型错误

            @Html.ValidationSummary(true)
Run Code Online (Sandbox Code Playgroud)

这个错误登录尝试的具体示例并不完全符合您的示例.

我找不到重定向用户的图像,因为它们未经授权,但在这种情况下,逻辑类似.错误消息可以使用QueryString,Viewbag等填充.


Lee*_*unn 5

我尝试了很多不同的方法,所有这些方法都有自己的偷偷摸摸的问题。不确定您的解决方案是如何设置的,但如果您需要执行传输,您可以使用以下方法:

自定义授权属性

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Server.TransferRequest("/Account/Login/?NOT_AUTHORISED=TRUE", false);
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要将消息传递给下一个请求,使用 QueryString 似乎可以解决问题。尽管您需要记住,任何人都可以设置 QS 参数。

设置状态码

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (Request["NOT_AUTHORISED"] == "TRUE")
    {
        Response.StatusCode = (int) HttpStatusCode.Unauthorized;
    }
}
Run Code Online (Sandbox Code Playgroud)

演示

没有重定向,HTTP 状态代码设置为 401。

在此处输入图片说明


tim*_*kly 5

理想情况下,您希望使用自定义AuthorizeAtrribute然后挂钩到该HandleUnauthorizedRequest方法,然后通过将控制器和视图设置为在AuthorizationContext中调用,将视图返回到登录页面.

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.RouteData.Values["controller"] = "home";
            filterContext.RouteData.Values["action"] = "login";

            filterContext.Result = new ViewResult ();
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
            filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:这样做的诀窍是确保将以下值设置为true,以避免在设置401状态后表单身份验证接管响应.

HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
Run Code Online (Sandbox Code Playgroud)