为什么AuthorizeAttribute会重定向到登录页面以进行身份​​验证和授权失败?

Rog*_*mbe 258 authentication asp.net-mvc authorization

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

[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这意味着,如果当前登录的用户不在"CanDeleteTags"角色中,则永远不会调用控制器方法.

不幸的是,对于失败,AuthorizeAttribute返回HttpUnauthorizedResult,它总是返回HTTP状态代码401.这导致重定向到登录页面.

如果用户未登录,则这非常有意义.但是,如果用户登录但未处于所需角色,则将其发送回登录页面会很困惑.

似乎AuthorizeAttribute将身份验证和授权混为一谈.

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

我不得不做一个DemandRoleAttribute将两者分开的东西.当用户未经过身份验证时,它会返回HTTP 401,并将其发送到登录页面.当用户登录但未处于所需角色时,它会创建一个NotAuthorizedResult.目前,这会重定向到错误页面.

当然我不必这样做?

Sha*_*ser 299

当它首次开发时,System.Web.Mvc.AuthorizeAttribute正在做正确的事情 - HTTP规范的旧版本使用状态代码401用于"未授权"和"未经验证".

从原始规格:

如果请求已包含授权凭据,则401响应表示已拒绝授权这些凭据.

事实上,你可以看到那里的混乱 - 当它意味着"认证"时,它使用"授权"一词.但是,在日常实践中,当用户通过身份验证但未获得授权时,返回403 Forbidden更有意义.用户不太可能拥有第二组凭据来提供访问权限 - 这些都是糟糕的用户体验.

考虑大多数操作系统 - 当您尝试读取您无权访问的文件时,您不会显示登录屏幕!

值得庆幸的是,HTTP规范已更新(2014年6月),以消除歧义.

从"超文本传输​​协议(HTTP/1.1):身份验证"(RFC 7235):

401(未授权)状态代码表示尚未应用请求,因为它缺少目标资源的有效身份验证凭据.

来自"超文本传输​​协议(HTTP/1.1):语义和内容"(RFC 7231):

403(禁止)状态代码表示服务器理解请求但拒绝授权.

有趣的是,在ASP.NET MVC 1发布时,AuthorizeAttribute的行为是正确的.现在,行为不正确 - 修复了HTTP/1.1规范.

而不是尝试更改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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1非常好的方法.一个小建议:不要检查`filterContext.HttpContext.User.Identity.IsAuthenticated`,而只需检查`filterContext.HttpContext.Request.IsAuthenticated`,它内置了空值检查.请参阅http://stackoverflow.com/问题/ 1379566 /什么,是最差间的HttpContext电流,请求isauthenticated和-HT/1379601#1379601 (50认同)
  • @DePeter规范从未说明重定向的内容,为什么重定向是一个更好的解决方案?仅此一项就可以在没有黑客攻击的情况下杀死ajax请求来解决它. (2认同)

Ala*_*son 24

将其添加到您的Login Page_Load功能:

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

当用户重定向到那里但已经登录时,它会显示未授权的页面.如果他们没有登录,则会显示登录页面.

  • Page_Load是一个webforms mojo (18认同)
  • 根据您的解决方案,如果您已经登录并通过键入URL转到"登录"页面...这将使您进入"未授权"页面.这是不对的. (3认同)
  • @Chance - 然后在默认的ActionMethod中为已调用FormsAuthencation调用的控制器执行此操作. (2认同)