认证授权失败时,AuthorizeAttribute为什么会重定向到登录页面?

在ASP。NET MVC中,你可以用AuthorizeAttribute标记一个控制器方法,像这样:

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

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

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

如果用户没有登录,这是完全有意义的。然而,如果用户是已经登录,但不是所需的角色,将他们发送回登录页面会令人困惑。

AuthorizeAttribute似乎合并了身份验证和授权。

这似乎是ASP中的一个疏忽。NET MVC,还是我错过了什么?

我不得不编写一个DemandRoleAttribute来分隔这两个对象。当用户未经过身份验证时,它返回HTTP 401,将用户发送到登录页面。当用户已登录,但不是所要求的角色时,它会创建一个NotAuthorizedResult。目前这将重定向到一个错误页面。

难道我没必要这么做吗?

98052 次浏览

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

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

(它不是MVC特有的)

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

在属性上添加一个附加参数来指定将未经授权的用户重定向到哪里可能会更好。但与此同时,我将AuthorizeAttribute视为一个安全网。

我一直觉得这是有道理的。如果您已登录,并试图点击一个需要您没有的角色的页面,则会被转发到登录屏幕,要求您使用具有该角色的用户登录。

您可以向登录页面添加逻辑,以检查用户是否已经通过身份验证。你可以添加一条友好的信息,解释为什么他们又被困在那里了。

添加到你的Login Page_Load函数:

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

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

当它第一次被开发时,System.Web.Mvc.AuthorizeAttribute正在做正确的事情- HTTP规范的旧版本同时使用状态码401表示“未授权”和“未认证”。< / p >

从原始规范:

如果请求已经包含了授权凭据,则401响应指示已拒绝对这些凭据进行授权。

事实上,你可以看到这里的混乱——它使用了“授权”这个词,而它的意思是“认证”。然而,在日常实践中,当用户经过身份验证但未获得授权时,返回403 Forbidden更有意义。用户不太可能有第二组凭据来给他们访问权限——这是糟糕的用户体验。

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

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

摘自“超文本传输协议(HTTP/1.1):认证”(RFC 7235):

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

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

403 (Forbidden)状态码表示服务器理解请求,但拒绝对其授权。

有趣的是,在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);
}
}
}

在全局的Application_EndRequest处理程序中试试这个。ascx文件

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
HttpContext.Current.Response.ClearContent();
Response.Redirect("~/AccessDenied.aspx");
}

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

在我的案例中,问题是“HTTP规范对“未经授权”和“未经身份验证”同时使用状态代码401”。正如ShadowChaser所说。

这个解决方案对我来说很有效:

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