简体   繁体   English

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

[英]How can I centralize entitlement logic in an asp.net-mvc controller?

I have an ASP.NET-MVC web site with a SQL Server back-end. 我有一个带有SQL Server后端的ASP.NET-MVC网站。 I have a number of controller actions that require me to do an entitlement check. 我有许多控制器操作,要求我进行权利检查。

Right now, I do this: 现在,我这样做:

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

this works fine but it feels like I am duplicating this logic in a number of places. 这工作正常,但感觉就像我在许多地方重复这个逻辑。

What is the best way (attribute, etc) to have a number of entitlement controller action "secure" based on the below? 根据以下内容,将许多权利控制器操作“安全”的最佳方式(属性等)是什么?

(Secure being that it checks the IsMySiteAdminRole and returns the "not Entitled" view if not entitled. (确保它检查IsMySiteAdminRole并返回“未授权”视图(如果没有IsMySiteAdminRole

I also want to make sure I don't have a performance penalty on every page? 我还想确保每个页面都没有性能损失?

I prefer to use action filters for Entitlement / Privilege logic. 我更喜欢为Entitlement / Privilege逻辑使用动作过滤器。 The beauty of these filters is they can run AFTER the action method populates your Model. 这些过滤器的优点是它们可以在动作方法填充模型运行。

For Example: 例如:

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

Action Filters allow you to selectively override your Controller's OnActionExecuted method. 动作过滤器允许您有选择地覆盖Controller的OnActionExecuted方法。

This attribute can be applied to a specific action or an entire controller. 此属性可以应用于特定操作或整个控制器。 The result will depend on your model values and will change your View only. 结果将取决于您的模型值,并将仅更改您的视图。

The best way to achieve this would be to use an attribute and decorate the actions with it. 实现这一目标的最佳方法是使用属性并使用它来装饰动作。

Take a look at System.Web.Mvc.AuthorizeAttribute 看看System.Web.Mvc.AuthorizeAttribute

You can inherit it and perform your own custom logic. 您可以继承它并执行自己的自定义逻辑。 Here is a sample from a project I did a while ago: 这是我刚才做的一个项目的样本:

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

With this attribute you can have two levels of authorization: regular user and admin user. 使用此属性,您可以拥有两个级别的授权:常规用户和管理员用户。 Example: 例:

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

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

I agree with everyone else here that you can accomplish this with attributes but I would just like to point out another alternative. 我同意其他所有人的意见,你可以用属性来完成这个任务,但我想指出另一种选择。 You could write a base controller from which all your controllers which require entitlement check inherit. 您可以编写一个基本控制器,您需要权限检查的所有控制器都从该控制器继承。 In that controller you could write your logic to check for entitlement: 在该控制器中,您可以编写逻辑来检查权利:

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

Now all you have to do is inherit this controller in all your controllers which need to have entitlement check: 现在,您所要做的就是在需要进行权利检查的所有控制器中继承此控制器:

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

I'm not arguing that this is a better option I'm just pointing out an alternative way of doing what you need. 我并不是说这是一个更好的选择,我只是指出了做你需要的另一种方式。 Additionally you could also implement some sort of caching in case you want to ensure that the entitlement check does not happen on every page request but only once when user has logged in... 此外,您还可以实现某种缓存,以防止您希望确保权限检查不会发生在每个页面请求上,但只有在用户登录时才会发生...

TL;DR: Look into expanding your user profile and filling it with role information when the user is authenticated. TL; DR:在用户通过身份验证时,查看扩展您的用户配置文件并填充角色信息。


To me, it sounds like the problem stems from the design of the security layer in that the user that you are checking is only available in the wrong stage of execution which is causing this complication. 对我来说,问题似乎源于安全层的设计,因为您正在检查的用户仅在执行错误的阶段可用,这导致了这种复杂性。 It looks like what you're trying to do is inspect the user making the request to see if he meets a specific role requirement and show them an unauthorized page if they do not meet the requirement (pretty textbooks stuff), but for some reason, that user is in the view model and that view model is not built until the controller is fully instantiated which means you can't check it is initialized in the pipeline. 看起来你要做的就是检查用户发出请求,看看他是否符合特定的角色要求,如果他们不符合要求(漂亮的教科书内容),则向他们展示未经授权的页面,但由于某种原因,该用户位于视图模型中,并且在完全实例化控制器之前不会构建该视图模型,这意味着您无法检查它是否已在管道中初始化。 The sloppy solution would be to override the initialize method on the controller, call the base method, but then do some work after the base initialize is complete. 草率的解决方案是覆盖控制器上的initialize方法,调用基本方法,但在基本初始化完成后再做一些工作。 Then your data should be ready to go. 那么你的数据应该准备好了。 But that's sloppy. 但那很草率。

Generally speaking, your session user should be available in the session in the form of a UserProfile or extended identity. 一般来说,会话​​用户应该以UserProfile或扩展标识的形式在会话中可用。 By doing so, you can fill that user's roles and what not and then check it at any stage of the controller/action execution pipeline; 通过这样做,您可以填充该用户的角色和不用的角色,然后在控制器/操作执行管道的任何阶段进行检查; then you can just use a custom attribute to check User.IsInRole("Whatever"). 然后你可以使用自定义属性来检查User.IsInRole(“Whatever”)。 You wouldn't need the action to return a "Not Entitled" view because you can set that in the response in the authorize attribute HandlUnauthorizedRequest override. 您不需要该操作返回“未授权”视图,因为您可以在授权属性HandlUnauthorizedRequest覆盖中的响应中设置该操作。

I do agree on the solutions suggested by others!! 我同意其他人提出的解决方案!!

What you mentioned is that you need to duplicate code, wherever you need to check the models IsInAdminRole property. 你提到的是你需要复制代码,无论你需要检查模型IsInAdminRole属性。 So I created a common method where you pass the Model and the viewname/path. 所以我创建了一个通用模型和视图名/路径的通用方法。 In the IsEntitled method just check the isadminrole property and take necessary action. 在IsEntitled方法中,只需检查isadminrole属性并采取必要的操作。

Note: This is just a simple and sample solution. 注意:这只是一个简单的示例解决方案。 You can check if this helps or gives some pointers. 您可以检查这是否有帮助或给出一些指示。

Do something as follows. 做一些如下事情。

Sample Models 样本模型

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

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

Here is the home controller 这是家庭控制器

public class HomeController : Controller
{

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

And here is a sample static method. 这是一个静态方法示例。 this can go in a helper class. 这可以进入帮助类。 Note: This will return a standard error view with the error message that is specified in the Message property of the NoRights Model. 注意:这将返回标准错误视图,其中包含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;
    }

If I pass on true in the IsAdminRole property here is the output. 如果我在IsAdminRole属性中传递true,那么输出就是。

到家指数

If I pass on false in the IsAdminRole property I get following output. 如果我在IsAdminRole属性中传递false,我会得到以下输出。

拒绝访问

Hope this helps. 希望这可以帮助。

We had a similar case wherein the requirement was the authorization logic to be set completely in one centralized location. 我们有类似的情况,其中要求是在一个集中位置完全设置的授权逻辑。 For this you can try something like this. 为此你可以尝试这样的事情。

First you can have an Application Level Controller which can be 首先,您可以拥有一个应用程序级别控制器

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

Now this controller can be inherited in other controllers. 现在这个控制器可以在其他控制器中继承。 Your entire entitlement logic is now centralized in one controller. 您的整个权利逻辑现在集中在一个控制器中。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM