在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
。目前,这会重定向到错误页面。
当然我不必这样做?
我一直认为这确实有意义。如果您已登录并尝试访问需要您没有的角色的页面,则会转到登录屏幕,要求您与具有该角色的用户一起登录。
您可以向登录页面添加逻辑,以检查用户是否已经过身份验证。你可以添加一条友好的信息,解释为什么他们再次被打破了。
不幸的是,您正在处理ASP.NET表单身份验证的默认行为。这里讨论了一种解决方法(我还没试过):
http://www.codeproject.com/KB/aspnet/Custon401Page.aspx
(它不是特定于MVC)
我认为在大多数情况下,最好的解决方案是在用户尝试到达之前限制对未经授权资源的访问。删除/删除可能将其带到此未授权页面的链接或按钮。
在属性上有一个额外的参数来指定重定向未授权用户的位置可能会很好。但与此同时,我将AuthorizeAttribute视为一个安全网。
在您的Global.asax文件的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");
}
如果你使用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" });
}