管理未经授权访问的 MVC 安全性和重定向






4.50/5 (7投票s)
当用户无法访问 MVC 中的控制器或控制器操作时,如何将安全性和重定向应用于视图。
引言
当用户无法访问 MVC 中的控制器或控制器动作时,应用安全性和重定向到视图。我选择以下解决方案是因为使用 ASP.NET 用户/权限的集成安全性是保护应用程序最简单的方法。此外,它非常适合第三方工具,例如 Telerik。例如,Telerik 菜单会自动调整,而无需编写任何代码,仅仅是因为用户没有访问控制器或动作的角色。
使用代码
我发现的最简单和最灵活的方法是创建一个新的属性,该属性继承自 System.Web.Mvc.AuthorizeAttribute
类。在那里,我添加了三个属性:ActionOrViewName、Controller 和 Area。您可以设置带有或不带有区域的视图名称,如果您在根目录或特定区域中有一个共享视图。或者,您可以设置一个动作和一个控制器,带有或不带有区域以重定向到动作。我修改了 HandleUnauthorizedRequest
方法的行为,如下所示
/// <summary>
/// Use this class instead of Authorize to redirect to a spcific view on unauthorized access.
/// If this attribute is used on a child action, it does the base result else, it redirect
/// to the specified view. The default view is UnauthorizedAccess, but can be overriden with
/// the ActionOrViewName property.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeWithRedirectToViewAttribute : AuthorizeAttribute
{
#region Private Fields
private const string DefaultActionOrViewName = "UnauthorizedAccess";
private string _actionOrViewName;
#endregion
#region Properties
/// <summary>
/// The name of the view to render on authorization failure. Default is
/// "UnauthorizedAccess".
/// </summary>
public string ActionOrViewName
{
get
{
return string.IsNullOrWhiteSpace(_actionOrViewName)
? DefaultActionOrViewName
: _actionOrViewName;
}
set { _actionOrViewName = value; }
}
public string Controller { get; set; }
public string Area { get; set; }
#endregion
#region Overrides
/// <summary>
/// Processes HTTP requests that fail authorization.
/// </summary>
/// <param name="filterContext"> Encapsulates the information for using
/// <see cref="T:System.Web.Mvc.AuthorizeAttribute" /> . The
/// <paramref name="filterContext" /> object contains the controller, HTTP context,
/// request context, action result, and route data. </param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.IsChildAction)
base.HandleUnauthorizedRequest(filterContext);
else
{
var factory = new HttpUnauthorizedWithRedirectToResultFactory();
filterContext.Result = factory.GetInstance(
Area,
Controller,
ActionOrViewName);
}
}
#endregion
}
这样,我不再显示空白页面,而是将其替换为自定义的 Mvc ActionResult
,该 ActionResult 继承自 System.Web.Mvc.HttpUnauthorizedResult
类。新的结果将获得渲染视图所需的所有参数。
public abstract class HttpUnauthorizedWithRedirectToResultBase : HttpUnauthorizedResult
{
protected ActionResult _result;
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (context.HttpContext.Request.IsAuthenticated)
{
context.HttpContext.Response.StatusCode = 200;
InitializeResult(context);
_result.ExecuteResult(context);
}
else
base.ExecuteResult(context);
}
protected abstract void InitializeResult(ControllerContext context);
}
public class HttpUnauthorizedWithRedirectToViewResult
: HttpUnauthorizedWithRedirectToResultBase
{
#region Ctors
public HttpUnauthorizedWithRedirectToViewResult(string viewName, string area)
{
_viewName = string.IsNullOrWhiteSpace(viewName) ? viewName : viewName.Trim();
_area = string.IsNullOrWhiteSpace(area) ? area : area.Trim();
}
#endregion
#region Private Fields
private readonly string _area;
private readonly string _viewName;
#endregion
#region Overrides of HttpUnauthorizedWithRedirectToResultBase
protected override void InitializeResult(ControllerContext context)
{
SetAreaRouteData(context);
_result = new ViewResult
{
ViewName = _viewName,
};
}
#endregion
#region Methods
private void SetAreaRouteData(ControllerContext context)
{
if (context.RequestContext.RouteData.DataTokens.ContainsKey("area"))
{
if (!string.IsNullOrWhiteSpace(_area))
context.RequestContext.RouteData.DataTokens["area"] = _area;
}
else
context.RequestContext.RouteData.DataTokens.Add("area", _area);
}
#endregion
}
public class HttpUnauthorizedWithRedirectToRouteResult
: HttpUnauthorizedWithRedirectToResultBase
{
#region Ctors
public HttpUnauthorizedWithRedirectToRouteResult(string action, string controller, string area)
{
_action = string.IsNullOrWhiteSpace(action) ? action : action.Trim();
_controller = string.IsNullOrWhiteSpace(controller) ? controller : controller.Trim();
_area = string.IsNullOrWhiteSpace(area) ? area : area.Trim();
}
#endregion
#region Private Fields
private readonly string _action;
private readonly string _area;
private readonly string _controller;
#endregion
#region Overrides of HttpUnauthorizedWithRedirectToResultBase
protected override void InitializeResult(ControllerContext context)
{
_result = new RedirectToRouteResult(new RouteValueDictionary
{
{"area", _area},
{"controller", _controller},
{"action", _action}
});
}
#endregion
}
public class HttpUnauthorizedWithRedirectToResultFactory
{
public HttpUnauthorizedWithRedirectToResultBase GetInstance(string area,
string controller, string actionOrViewName)
{
if (string.IsNullOrWhiteSpace(actionOrViewName))
throw new ArgumentException("You must set an actionOrViewName");
if(string.IsNullOrWhiteSpace(controller) )
return new HttpUnauthorizedWithRedirectToViewResult(actionOrViewName, area);
return new HttpUnauthorizedWithRedirectToRouteResult(actionOrViewName, controller, area);
}
}
我修改了 ExecuteResult
方法的行为,以获得与 ViewResult
或 RedirectToRouteResult
相同的反应。在这种情况下,重定向视图不接收任何值,它只显示一条通用消息,但它可以更复杂。为此,您只需添加一个模型或使用视图包即可。
为什么要从 HttpUnauthorizedResult
类继承?为什么不直接在 HandleUnauthorizedRequest
方法中返回一个 ViewResult?因为我正在使用 Telerik 第三方库,Telerik 会验证获得的结果是否为 HttpUnauthorizedResult
,以显示或隐藏菜单项。
重要的是要验证 filterContext.IsChildAction
,以保留子动作的预期结果。这样,当用户没有权限时,MVC 将简单地隐藏子部分。
在应用程序中,该属性放置在类或动作方法的声明上。
[AuthorizeWithRedirectToView(Roles = "ReaderRole,WriterRole,SpecialRole")]
public class MyController : Controller
{
[AuthorizeWithRedirectToView(Roles = " ReaderRole , WriterRole , SpecialRole "),
Controller = "Security", Area = "MyArea")]
public ViewResult Details(Guid id)
{
…
}
[ChildActionOnly]
[AuthorizeWithRedirectToView(Roles = " SpecialRole ")]
public PartialViewResult ChildDetails(Guid id)
{
…
}
// To define a view other than the default "UnauthorizedAccess".
[AuthorizeWithRedirectToView(Roles = " WriterRole ", ActionOrViewName = "OtherUnauthorizedAccess")]
public ViewResult Details(ViewModel viewModel)
{
…
}
}
UnauthorizedAccess.cshtml
@{
ViewBag.Title = "Unauthorized Access";
}
<h2>Unauthorized access.</h2>
<p>
Sorry, you lack privileges to access this page. <a href="javascript:history.go(-1)">Click here</a> to go back.
</p>
结论...
我希望这篇文章对您有所帮助,或者在未来对您有所帮助。我调整了我的解决方案,以支持在 Sql Compact 数据库上运行的 Asp.Net 基于角色的安全性,并以独立模式运行。如果您有任何建议或评论,请随时提出!您可以在此处下载项目的完整版本。