为什么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"); }