如何在asp.net-mvc控制器中集中授权逻辑?

时间:2014-03-12 16:48:31

标签: asp.net asp.net-mvc asp.net-mvc-4 asp.net-mvc-5 entitlements

我有一个带有 SQL Server 后端的 ASP.NET-MVC 网站。我有许多控制器操作,要求我进行权利检查。

现在,我这样做:

    public ActionResult SomeEntitledPage()
    {
        if (_myModel.IsMySiteAdminRole)
        {
            return View(new MyViewModel());
        }
        else
        {
            return View("NotEntitled", new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "support@support.com"});
        }
    }

这很好用,但感觉我在很多地方重复了这个逻辑。

拥有多项授权控制器操作的最佳方式(属性等)是什么?#34; secure"基于以下内容?

(确保它检查IsMySiteAdminRole并返回"未授权"视图(如果没有权限)。

我还想确保每页都没有性能损失?

6 个答案:

答案 0 :(得分:5)

我更喜欢为权利/特权逻辑使用动作过滤器。这些过滤器的优点是它们可以运行 AFTER 操作方法填充模型。

例如:

public class AdminOnlyFilterAttribute : ActionFilterAttribute
{
      public override void OnActionExecuted(ActionExecutedContext filterContext)
      {
        if (!filterContext.Controller.ViewData.Model.IsMySiteAdminRole)
            {
                filterContext.Result = new ViewResult
                {
                    ViewName = "NotEntitled",
                    Model = new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "support@support.com"}
                };
            }
        base.OnActionExecuted(filterContext);
    }   
}

动作过滤器允许您有选择地覆盖Controller的OnActionExecuted方法。

此属性可应用于特定操作或整个控制器。结果将取决于您的模型值,并将仅更改您的视图。

答案 1 :(得分:0)

实现这一目标的最佳方法是使用属性并使用它来装饰动作。

查看System.Web.Mvc.AuthorizeAttribute

您可以继承它并执行自己的自定义逻辑。以下是我之前做过的项目的示例:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    public bool AdminRequired;
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (UserSession.IsLoggedIn)
            return (!AdminRequired || (AdminRequired && UserSession.IsAdmin));
        else
            return false;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new RedirectToRouteResult(
                                   new RouteValueDictionary 
                                   {
                                       { "action", "LogIn" },
                                       { "controller", "Account" },
                                       { "returnUrl", filterContext.HttpContext.Request.RawUrl}
                                   });
    }
}

使用此属性,您可以拥有两个级别的授权:常规用户和管理员用户。例如:

[Authorize(AdminRequired = true)]
public ActionResult AdminOnlyAction()
{
    // perform authorized admin tasks
}

[Authorize]
public ActionResult RegularUserAction()
{
    // perform authorized regular user action
}

答案 2 :(得分:0)

我同意其他所有人的意见,你可以通过属性实现这一点,但我想指出另一种选择。您可以编写一个基本控制器,您需要权限检查的所有控制器都从该控制器继承。在该控制器中,您可以编写逻辑来检查权利:

public class BaseController : Controller
{
    private readonly bool _isEntitled;

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        // Your logic for entitlement check goes here.
        // Set _isEntitled to true if user is entitled to view page.
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!_isEntitled) {
           // Redirect user which is not entitled.
           filterContext.Result = new ViewResult
                                  {
                                     ViewName = "NotEntitled",
                                     Model = new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "support@support.com"}
                                  };
        }
    }
}

现在你所要做的就是在你需要进行权利检查的所有控制器中继承这个控制器:

  public MyController : BaseController
  {
      // Action in here...
  }

我并不是说这是一个更好的选择,我只是指出了另一种做你需要的方式。此外,您还可以实现某种缓存,以防止您确保在每个页面请求中都不会发生权利检查,但只有在用户登录时才会发生一次......

答案 3 :(得分:0)

TL; DR: 在用户通过身份验证时,请考虑扩展您的用户配置文件并填充角色信息。


对我来说,问题似乎是问题源于安全层的设计,因为您正在检查的用户只能在错误的执行阶段使用,这会导致这种复杂化。 看起来就像你正在尝试做的那样是检查提出请求的用户是否符合特定的角色要求,如果他们不符合要求,则向他们展示未经授权的页面(漂亮)教科书的东西),但由于某种原因,该用户在视图模型中,并且在控制器完全实例化之前不构建视图模型,这意味着您无法检查它是否在管道中初始化。草率的解决方案是覆盖控制器上的initialize方法,调用基本方法,但在基本初始化完成后再做一些工作。那么你的数据应该准备好了。但那很草率。

一般来说,您的会话用户应该以UserProfile或扩展身份的形式在会话中可用。通过这样做,您可以填充该用户的角色以及未填充的角色,然后在控制器/操作执行管道的任何阶段进行检查;然后你可以使用自定义属性来检查User.IsInRole(" Whatever")。您不需要该操作来返回" Not Entitled" view,因为您可以在authorize属性HandlUnauthorizedRequest覆盖中的响应中设置它。

答案 4 :(得分:0)

我同意其他人建议的解决方案!!

您提到的是,您需要复制代码,无论您需要检查模型IsInAdminRole属性。所以我创建了一个通用模型和视图名/路径的通用方法。在IsEntitled方法中,只需检查isadminrole属性并采取必要的操作。

注意:这只是一个简单的示例解决方案。您可以检查这是否有帮助或给出一些指示。

执行以下操作。

样本模型

public class NoRights
{
    public string Message { get; set; }
}

public class MyModel
{
    public bool IsAdminRole { get; set; }
}

这是家庭控制器

public class HomeController : Controller
{

    public ActionResult Index()
    {
        var mod = new MyModel() { IsAdminRole = true };
        return IsEntitled(mod, "IndeX");
        //return View();
    }
}

这是一个静态方法示例。这可以进入帮助类。 注意:这将返回标准错误视图,其中包含NoRights模型的Message属性中指定的错误消息。

    public static ViewResult IsEntitled(object model, string viewPath)
    {
        var prop = model.GetType().GetProperty("IsAdminRole");
        var hasRights = (bool)prop.GetValue(model, null);
        var viewResult = new ViewResult();
        if (hasRights)
        {
            viewResult.ViewData = new ViewDataDictionary(model);
            viewResult.ViewName = viewPath;
        }
        else
        {
            viewResult.ViewData = new ViewDataDictionary(
                new NoRights() { Message = "Your dont have rights" });
            viewResult.ViewName = "Error";

        }
        return viewResult;
    }

如果我在IsAdminRole属性中传递true,则输出为

To Home Index

如果我在IsAdminRole属性中传递false,我会得到以下输出。

Access denied

希望这会有所帮助。

答案 5 :(得分:0)

我们有类似的情况,其中要求是在一个集中位置完全设置的授权逻辑。为此你可以尝试这样的事情。

首先,您可以拥有一个可以

的应用程序级别控制器
public class ApplicationController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext ctx)
    {
        base.OnActionExecuted(ctx);

        if (!_myModel.IsMySiteAdminRole)
        {
            ctx.Result = View("NotEntitled", new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "support@support.com"});
        }
    }
}

现在这个控制器可以在其他控制器中继承。您的整个权利逻辑现在集中在一个控制器中。

public class EntitlementController: ApplicationController
{
    return View(new MyViewModel());
}