65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (7投票s)

2012年3月9日

CPOL

2分钟阅读

viewsIcon

58559

downloadIcon

1429

当用户无法访问 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 方法的行为,以获得与 ViewResultRedirectToRouteResult 相同的反应。在这种情况下,重定向视图不接收任何值,它只显示一条通用消息,但它可以更复杂。为此,您只需添加一个模型或使用视图包即可。

为什么要从 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 基于角色的安全性,并以独立模式运行。如果您有任何建议或评论,请随时提出!您可以在此处下载项目的完整版本。

© . All rights reserved.