在ASP.NET MVC中优化角色授权

时间:2017-07-13 15:04:44

标签: c# asp.net-mvc active-directory owin

我正在为我的公司制作一个asp.NET MVC5内部网网站,因为我刚刚根据Active Directory中的用户角色完成了授权和动态菜单,我需要对访问应用限制。我在MSDN / Technet上看过,您可以使用[Authorize(Role="<YourRole>")]来应用授权,并使其完美运行。现在我的问题是我有20个不同的角色,每个角色都与我的MSSQL数据库中的DocumentsCategories相关联。这意味着如果要访问文档或资源,必须先在DB中找到相应的条目(如果需要,我可以进一步解释)。因此,如果我使用[Authorize]属性实现授权,我将必须检查我的数据库以查看该行是否存在,以及是否添加它。我已经开始使用静态类:

public static class CustomRoles
{
    public const string Role1 = "Role1";
    public const string Role2 = "Role2";
    //And so on ...
}

然后在我的控制器动作方法中:

[Authorize(Roles=CustomRoles.Role1+","+CustomRoles.Role2)]
public ActionResult Index(){}

你可以想象为每个角色做这件事将是漫长而乏味的。

所以我的问题:您知道更好/更简单的方法吗?因为我必须手动检查每个文档(数千!),然后在另一个表中查找关联的配置文件,然后应用相应的授权。从技术上讲,我的动态菜单应该处理这个,因为你看不到你没有的东西,但是再次,如果没有实现授权,你可以用这种方式访问​​任何东西

还有:并非所有角色都在我的数据库中注册,大多数用户拥有大约140个角色,但数据库中可能只有1或2个角色。这会产生一些性能问题吗?我知道我可以在创建Claims时处理此问题,并过滤掉不属于数据库的那些但我不愿意这样做。

1 个答案:

答案 0 :(得分:1)

您可以执行的一种解决方法是使用OnActionExecuting中的重叠ActionFilter方法而不是装饰器AuthorizeAttribute来检查用户是否有权执行某项操作。

是的,您仍然需要仔细检查您的角色授权,但并非稀疏,因为您只需要检查所有一个的地方,即您的操作过滤器(或者更确切地说,您的 switch)。此外,将所有内容放在一个位置使您可以避免冗余,并尽可能使逻辑更紧凑。

示例:

ActionFilter

public class AuthorizeActionFilterAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(ActionExecutingContext filterContext) {

        IPrincipal user = HttpContext.Current.User; //get the current user

        //Get controller name and action
        string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
        string actionName = filterContext.ActionDescriptor.ActionName;

        //All roles will be defined here!
        List<string> rolesAccepted = new List<string>();
        switch (controllerName){
            case "Controller1": //suppose these three happen to have the same rules
            case "Controller2":
            case "Controller3":
                //Describe roles accepted for certain controller and action
                rolesAccepted = new List<string> { "Role1", "Role2" };            
                break;
            case "Controller4": //suppose these have specific restrictions only for some actions
                if (actionName == "action1") {//can also use switch 
                    rolesAccepted = new List<string> { "Role3", "Role4" };
                } else {
                    rolesAccepted = new List<string> { "Role5", "Role6", "Role7" };
                }
                break;
            ....
        }

        //Redirect to login if non of the user role is authorized
        if (!rolesAccepted.Any(x => user.IsInRole(x)){
            filterContext.Result = redirectToLogin();
            return;
        }
    }

    private ActionResult redirectToLogin() {
      return new RedirectToRouteResult(
          new RouteValueDictionary(new { controller = "Account", action = "Login" })
        );
    }

}

然后您只需将AuthorizeActionFilterAttribute放在每个控制器中(而不是每个操作中的角色),因为您已在单个位置处理了所有授权:

[AuthorizeActionFilter]
public class Controller1 : Controller {
    ...
}

[AuthorizeActionFilter]
public class Controller2 : Controller {
    ...
}

... and so on