优化 ASP.NET MVC 中的角色授权
Optimize Roles authorization in ASP.NET MVC
我正在为我的公司制作一个 asp.NET MVC5 Intranet 网站,因为我刚刚根据 Active Directory 中的用户角色实现了授权和动态菜单,我需要应用访问限制。我在 MSDN / Technet 上读到,您可以使用 [Authorize(Role="<YourRole>")]
申请授权,我让它完美地工作。现在我的问题是我有 20 个不同的角色,每个角色都与我的 MSSQL 数据库中的 Documents
和 Categories
相关联。这意味着如果你想访问文档或资源,你必须首先在数据库中找到相应的条目(如果你需要我可以进一步解释)。因此,如果我使用 [Authorize]
属性实现授权,我将必须检查我的数据库以查看该行是否存在,以及是否添加它。我已经开始使用静态 class :
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(){}
您可以想象,为每个角色都这样做会很冗长乏味。
所以我的问题是:你知道有更好/更简单的方法吗?因为我必须手动检查每个文档(数千个!),然后在另一个 table 中查看关联了哪些配置文件,然后应用相应的授权。从技术上讲,我的动态菜单应该会解决这个问题,因为您看不到您不可用的内容,但是话又说回来,如果未实施授权,您可以使用 URL 以这种方式访问任何内容
还有: 并非所有角色都在我的数据库中注册,大多数用户有大约 140 个角色,但可能只有 1 或 2 个角色在数据库中注册。这会产生一些性能问题吗?我知道当我创建 Claims
并过滤掉不属于数据库的那些时我可以处理这个问题,但我不想这样做。
您可以做的一个解决方法是在 ActionFilter
中使用覆盖的 OnActionExecuting
方法而不是装饰器 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
我正在为我的公司制作一个 asp.NET MVC5 Intranet 网站,因为我刚刚根据 Active Directory 中的用户角色实现了授权和动态菜单,我需要应用访问限制。我在 MSDN / Technet 上读到,您可以使用 [Authorize(Role="<YourRole>")]
申请授权,我让它完美地工作。现在我的问题是我有 20 个不同的角色,每个角色都与我的 MSSQL 数据库中的 Documents
和 Categories
相关联。这意味着如果你想访问文档或资源,你必须首先在数据库中找到相应的条目(如果你需要我可以进一步解释)。因此,如果我使用 [Authorize]
属性实现授权,我将必须检查我的数据库以查看该行是否存在,以及是否添加它。我已经开始使用静态 class :
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(){}
您可以想象,为每个角色都这样做会很冗长乏味。
所以我的问题是:你知道有更好/更简单的方法吗?因为我必须手动检查每个文档(数千个!),然后在另一个 table 中查看关联了哪些配置文件,然后应用相应的授权。从技术上讲,我的动态菜单应该会解决这个问题,因为您看不到您不可用的内容,但是话又说回来,如果未实施授权,您可以使用 URL 以这种方式访问任何内容
还有: 并非所有角色都在我的数据库中注册,大多数用户有大约 140 个角色,但可能只有 1 或 2 个角色在数据库中注册。这会产生一些性能问题吗?我知道当我创建 Claims
并过滤掉不属于数据库的那些时我可以处理这个问题,但我不想这样做。
您可以做的一个解决方法是在 ActionFilter
中使用覆盖的 OnActionExecuting
方法而不是装饰器 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