首页 文章

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

提问于
浏览
250

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

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

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

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

如果用户未登录,则这非常有意义 . 但是,如果用户已经登录,但是并不容易将他们发送回登录页面 .

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

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

我不得不做一个将两者分开的 DemandRoleAttribute . 当用户不是所需角色时,它会创建一个 NotAuthorizedResult . 目前,这会重定向到错误页面 .

当然我不必这样做?

6 回答

  • 4

    当它首次开发时,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 's login page redirects, it'更容易只是在源头修复问题 . 您可以创建一个具有相同名称的新属性( AuthorizeAttributein your website's default namespace (这非常重要)然后编译器会自动选择它而不是MVC 's standard one. Of course, you could always give the attribute a new name if you' d而是采用这种方法 .

    [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);
            }
        }
    }
    
  • 4

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

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

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

  • 0

    我一直认为这确实有意义 . 如果您已登录并尝试访问需要您没有的角色的页面,则会转到登录屏幕,要求您与具有该角色的用户一起登录 .

    您可以向登录页面添加逻辑,以检查用户是否已经过身份验证 . 你可以添加一条友好的信息,解释为什么他们再次被打破了 .

  • 0

    不幸的是,您正在处理ASP.NET表单身份验证的默认行为 . 这里讨论了一种解决方法(我还没试过):

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

    (它不是特定于MVC)

    我认为在大多数情况下,最好的解决方案是在用户尝试到达之前限制对未经授权资源的访问 . 删除/删除可能将其带到此未授权页面的链接或按钮 .

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

  • 290

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

    如果你使用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;
                }
            }
        }
    }
    

相关问题