为什么AuthorizeAttributeredirect到login页面以进行身份​​validation和授权失败?

在ASP.NET MVC中,您可以使用AuthorizeAttribute标记控制器方法,如下所示:

 [Authorize(Roles = "CanDeleteTags")] public void Delete(string tagName) { // ... } 

这意味着,如果当前login的用户不在“CanDeleteTags”angular色中,控制器方法将永远不会被调用。

不幸的是,对于失败, AuthorizeAttribute返回HttpUnauthorizedResult ,它始终返回HTTP状态代码401.这会导致redirect到login页面。

如果用户没有login,这是非常有道理的。 但是,如果用户已经login,但不是所需的angular色,将它们发送回login页面会令人困惑。

AuthorizeAttribute似乎将authentication和授权混为一谈。

这似乎是在ASP.NET MVC的一个疏忽,或者我错过了什么?

我不得不做一个DemandRoleAttribute来区分这两者。 当用户未通过身份validation时,会返回HTTP 401,并将其发送到login页面。 当用户login但不是所需的angular色时,它将创build一个NotAuthorizedResult 。 目前这个redirect到一个错误页面。

当然,我不必这样做?

当它第一次被开发时,System.Web.Mvc.AuthorizeAttribute做的是正确的事情 – 对于“未授权”和“未authentication”,HTTP规范的旧版本使用状态码401。

从原来的规格:

如果请求已经包含了授权凭证,那么401响应表明这些凭证的授权已被拒绝。

事实上,你可以看到那里的混乱 – 当它表示“authentication”时,它使用“授权”一词。 然而,在日常的实践中,当用户通过authentication但没有授权的情况下返回403 Forbidden更有意义。 用户不太可能拥有第二组凭据,这将使用户获得访问权限 – 用户体验不佳。

考虑大多数操作系统 – 当你试图读取一个你没有权限访问的文件时,你不会显示login屏幕!

谢天谢地,更新了HTTP规范(2014年6月),以消除模糊性。

从“超文本传输​​协议(HTTP / 1.1):authentication”(RFC 7235):

401(未授权)状态码指示该请求尚未应用,因为它缺less有效的目标资源的身份validation凭据。

从“超文本传输​​协议(HTTP / 1.1):语义和内容”(RFC 7231):

403(禁止)状态码指示服务器了解请求,但拒绝授权。

有趣的是,在ASP.NET MVC 1发布的时候,AuthorizeAttribute的行为是正确的。 现在,这个行为是不正确的 – HTTP / 1.1规范已经修复。

而不是试图改变ASP.NET的login页面redirect,只是从源头上解决问题更容易。 你可以在你的网站的默认命名空间 (这是非常重要的)中创build一个具有相同名称( 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); } } } 

添加到您的loginPage_Loadfunction:

 // User was redirected here because of authorization section if (User.Identity != null && User.Identity.IsAuthenticated) Response.Redirect("Unauthorized.aspx"); 

当用户在那里redirect,但已经login,它显示未经授权的页面。 如果他们没有login,它将通过并显示login页面。

我一直认为这是有道理的。 如果您已经login,并且尝试点击需要您没有的angular色的页面,则会转到login屏幕,要求您使用具有该angular色的用户login。

您可能会添加逻辑到login页面,检查用户是否已经通过身份validation。 你可以添加一个友好的信息,解释他们为什么再次被愚弄回来。

不幸的是,你正在处理ASP.NET表单身份validation的默认行为。 有一个解决方法(我没有尝试过)在这里讨论:

http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

(这不是特定于MVC)

我认为在大多数情况下,最好的解决scheme是在用户试图到达那里之前,限制对未授权资源的访问。 通过删除/灰色的链接或button,可能带他们到这个未经授权的页面。

如果在属性上有一个额外的参数来指定redirect未授权用户的位置,可能会很好。 但与此同时,我将AuthorizeAttribute视为一个安全网。

在您的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"); }