asp.net-mvc 为什么 AuthorizeAttribute 会重定向到登录页面以进行身份验证和授权失败?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/238437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?
提问by Roger Lipscombe
In ASP.NET MVC, you can mark up a controller method with AuthorizeAttribute, like this:
在 ASP.NET MVC 中,您可以使用 标记控制器方法AuthorizeAttribute,如下所示:
[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
// ...
}
This means that, if the currently logged-in user is not in the "CanDeleteTags" role, the controller method will never be called.
这意味着,如果当前登录的用户不在“CanDeleteTags”角色中,则永远不会调用控制器方法。
Unfortunately, for failures, AuthorizeAttributereturns HttpUnauthorizedResult, which always returns HTTP status code 401. This causes a redirection to the login page.
不幸的是,对于失败,AuthorizeAttribute返回HttpUnauthorizedResult,它总是返回 HTTP 状态代码 401。这会导致重定向到登录页面。
If the user isn't logged in, this makes perfect sense. However, if the user is alreadylogged in, but isn't in the required role, it's confusing to send them back to the login page.
如果用户没有登录,这是完全合理的。但是,如果用户已经登录,但不是所需的角色,将他们发送回登录页面会令人困惑。
It seems that AuthorizeAttributeconflates authentication and authorization.
似乎AuthorizeAttribute将身份验证和授权混为一谈。
This seems like a bit of an oversight in ASP.NET MVC, or am I missing something?
这在 ASP.NET MVC 中似乎有点疏忽,还是我遗漏了什么?
I've had to cook up a DemandRoleAttributethat separates the two. When the user isn't authenticated, it returns HTTP 401, sending them to the login page. When the user is logged in, but isn't in the required role, it creates a NotAuthorizedResultinstead. Currently this redirects to an error page.
我不得不做一个DemandRoleAttribute将两者分开的东西。当用户未通过身份验证时,它会返回 HTTP 401,将它们发送到登录页面。当用户登录但不是所需角色时,它会创建一个NotAuthorizedResult替代。目前这会重定向到错误页面。
Surely I didn't have to do this?
我当然不必这样做吗?
回答by ShadowChaser
When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing - older revisions of the HTTP specification used status code 401 for both "unauthorized" and "unauthenticated".
最初开发时,System.Web.Mvc.AuthorizeAttribute 正在做正确的事情 - HTTP 规范的旧版本使用状态代码 401 表示“未经授权”和“未经身份验证”。
From the original specification:
从原始规范:
If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.
如果请求已包含授权凭据,则 401 响应指示对这些凭据的授权已被拒绝。
In fact, you can see the confusion right there - it uses the word "authorization" when it means "authentication". In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It's unlikely the user would have a second set of credentials that would give them access - bad user experience all around.
事实上,您可以在那里看到混淆 - 当它的意思是“身份验证”时,它使用了“授权”这个词。然而,在日常实践中,当用户通过身份验证但未授权时返回 403 Forbidden 更有意义。用户不太可能拥有第二组凭据来授予他们访问权限 - 到处都是糟糕的用户体验。
Consider most operating systems - when you attempt to read a file you don't have permission to access, you aren't shown a login screen!
考虑大多数操作系统 - 当您尝试读取您无权访问的文件时,您不会看到登录屏幕!
Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.
幸运的是,HTTP 规范已更新(2014 年 6 月)以消除歧义。
From "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):
来自“超文本传输协议(HTTP/1.1):身份验证”(RFC 7235):
The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
401(未授权)状态代码表示该请求尚未应用,因为它缺少目标资源的有效身份验证凭据。
From "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content" (RFC 7231):
来自“超文本传输协议(HTTP/1.1):语义和内容”(RFC 7231):
The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.
403(禁止)状态码表示服务器理解请求但拒绝授权。
Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect - the HTTP/1.1 specification was fixed.
有趣的是,在 ASP.NET MVC 1 发布时,AuthorizeAttribute 的行为是正确的。现在,行为不正确 - HTTP/1.1 规范已修复。
Rather than attempt to change ASP.NET's login page redirects, it's easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace(this is very important) then the compiler will automatically pick it up instead of MVC's standard one. Of course, you could always give the attribute a new name if you'd rather take that approach.
与其尝试更改 ASP.NET 的登录页面重定向,不如从源头解决问题更容易。您可以在您网站的默认命名空间中创建一个具有相同名称 ( AuthorizeAttribute)的新属性(这非常重要),然后编译器将自动选择它而不是 MVC 的标准属性。当然,如果您愿意采用这种方法,您始终可以为该属性指定一个新名称。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
回答by Alan Hymanson
Add this to your Login Page_Load function:
将此添加到您的登录 Page_Load 函数中:
// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
Response.Redirect("Unauthorized.aspx");
When the user is redirected there but is already logged in, it shows the unauthorized page. If they are not logged in, it falls through and shows the login page.
当用户被重定向到那里但已经登录时,它会显示未经授权的页面。如果他们没有登录,它会失败并显示登录页面。
回答by Keltex
Unfortunately, you're dealing with the default behavior of ASP.NET forms authentication. There is a workaround (I haven't tried it) discussed here:
不幸的是,您正在处理 ASP.NET 表单身份验证的默认行为。这里讨论了一种解决方法(我还没有尝试过):
http://www.codeproject.com/KB/aspnet/Custon401Page.aspx
http://www.codeproject.com/KB/aspnet/Custon401Page.aspx
(It's not specific to MVC)
(它不是特定于 MVC 的)
I think in most cases the best solution is to restrict access to unauthorized resources prior to the user trying to get there. By removing/graying out the link or button that might take them to this unauthorized page.
我认为在大多数情况下,最好的解决方案是在用户尝试访问之前限制对未授权资源的访问。通过删除/变灰可能会将他们带到此未经授权的页面的链接或按钮。
It probably would be nice to have an additional parameter on the attribute to specify where to redirect an unauthorized user. But in the meantime, I look at the AuthorizeAttribute as a safety net.
在属性上有一个额外的参数来指定将未经授权的用户重定向到哪里可能会很好。但与此同时,我将 AuthorizeAttribute 视为安全网。
回答by Rob
I always thought this did make sense. If you're logged in and you try to hit a page that requires a role you don't have, you get forwarded to the login screen asking you to log in with a user who does have the role.
我一直认为这确实有道理。如果您已登录并尝试点击需要您没有的角色的页面,您将被转至登录屏幕,要求您使用具有该角色的用户登录。
You might add logic to the login page that checks to see if the user is already authenticated. You could add a friendly message that explains why they've been bumbed back there again.
您可以向登录页面添加逻辑,以检查用户是否已通过身份验证。您可以添加一条友好的消息,解释为什么他们再次被撞回那里。
回答by Kareem Cambridge
Try this in your in the Application_EndRequest handler of your Global.ascx file
在 Global.ascx 文件的 Application_EndRequest 处理程序中试试这个
if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
HttpContext.Current.Response.ClearContent();
Response.Redirect("~/AccessDenied.aspx");
}
回答by Greg Gum
If your using aspnetcore 2.0, use this:
如果您使用 aspnetcore 2.0,请使用:
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Core
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeApiAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
context.Result = new UnauthorizedResult();
return;
}
}
}
}
回答by César León
In my case the problem was "HTTP specification used status code 401 for both "unauthorized" and "unauthenticated"". As ShadowChaser said.
就我而言,问题是“HTTP 规范对“未经授权”和“未经身份验证”都使用了状态代码 401”。正如ShadowChaser所说。
This solution works for me:
这个解决方案对我有用:
if (User != null && User.Identity.IsAuthenticated && Response.StatusCode == 401)
{
//Do whatever
//In my case redirect to error page
Response.RedirectToRoute("Default", new { controller = "Home", action = "ErrorUnauthorized" });
}

