.NET MVC:根据角色级别限制操作
.NET MVC: Restrict action based on Role level
我想知道是否有更好的方法:
假设我有角色为 "SuperUser"、Admin、"Manager" 的用户, "Registered".
站点中注册的所有用户都具有 "Registered" 角色(例如 "Manager" 用户也具有 "Registered" 角色)。
现在我必须管理用户控制器上的 Delete 操作。我想达到的是:
- "Registered" 用户只能删除自己(即:从站点注册中删除)
- "Manager"用户可以删除自己和"Registered"用户,bug不能删除"SuperUser"和"Admin"
- "Admin"用户可以删除自己,"Managers"和"Registered"用户,bug不能删除"SuperUser"
- "SuperUser" 用户可以删除所有用户角色,包括超级用户。
所以我从以下代码开始:
[Authorize(Roles="Registered")]
public void Delete(int id)
{
string[] AllowedRoles = { "SuperAdmin", "Manager" };
if (_identity.FindFirst(ClaimTypes.UserData).Value == id.ToString())
{
//USER can delete himself!
//TO DO: Deletion code
}
else if (User.IsInAnyRole(AllowedRoles))
{
//CHECK IF I CAN DELETE THE GIVEN USER
}
}
我要做的是检查当前用户的每个角色和要删除的用户,但我真的不喜欢写很多"if"...
有更好的方法吗?
谢谢!
PS:不用担心 User.IsInAnyRole(这是一个自定义函数,用于验证用户是否属于指定角色之一。
与我所做的类似的场景,而不是将角色作为字符串然后作为枚举标志
[Flags]
public enum Permissions
{
None = 0,
Registered = 1 << 0,
SuperAdmin = 1 << 1,
Manager = 1 << 2,
// Etc...
}
然后,您可以使用此方法对用户确定允许哪些角色:
public bool IsInRole(Permissions roles)
{
var rolesToCheck = roles.GetFlags().Where(p => p != Permissions.None);
return rolesToCheck.Any(role => Roles.HasFlag(role));
}
和:
if(User.IsInRole(Permissions.SuperAdmin | Permissions.Manager)
{
// Do something
}
同样,您也可以通过创建 Authorize 属性的自定义扩展将其添加到属性中:
public class CustomAuthorize : ActionFilterAttribute, IActionFilter
{
public Permissions Roles { get; set; }
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
bool authorized = false;
var roleFlags = Roles.GetFlags();
if (!roleFlags.All(r => r == Permissions.None))
{
foreach (var role in roleFlags.Where(p => p != RolePermissions.None))
{
if (maritimeUser.Roles.HasFlag(role))
{
authorized = true;
}
}
}
if (Roles == Permissions.None)
{
// No roles set, so authorise = okay
return;
}
if (!authorized)
{
filterContext.Result =
new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Account"},
{"action", "Unauthorised"},
{"area", "" }
});
return;
}
}
并由
使用
[CustomAuthorize(Roles = Permissions.SuperAdmin | Permissions.Manager)]
我想知道,是否有经过身份验证的用户不是 "registered"? IMO 这个角色不是必需的。如果您不同意,您可以修改下面的代码。
我不确定你的代码中的 _identity
和 User
是什么,但我假设 _identity
是用户管理器存储库,User
是当前的 httpcontext用户。我假设您需要 UserManager,因为如果不访问存储的声明(如 AspNetUserClaims),您将无法执行此测试。
请注意,我没有完全测试这段代码。
// using System.Collections.Generic;
// using System.Linq;
// using System.Security.Claims;
// This method is available for all authenticated users
[Authorize]
public void Delete(int id)
{
// Test if current user wants to delete itself
if (User.FindFirst(ClaimTypes.UserData).Value != id.ToString())
{
// Find all roles of the current user.
var roles = User.FindAll("role").Select(r => r.Value).ToList();
// A fixed list, ordered by importance
var allowedRoles = new List<string> { "SuperAdmin", "Admin", "Manager" };
// Highest role of the current user
var role = allowedRoles.Intersect(roles).FirstOrDefault();
// "Registered" user is not allowed to do anything with other users
if (role == null)
return;
// Get the rolename(s) of the target user. Something like this, where
// _identity is a repository (usermanager?) that has access to the database
var targetUserRoles = _identity.Where(u => u.Id == id).Roles().Select(r => r.Name).ToList();
//var targetUserRoles = new List<string> { "Admin" };
// Highest role of the target user, because you don't want to delete
// a user that is both Manager and SuperAdmin when you are Admin.
var targetUserRole = allowedRoles.Intersect(targetUserRoles).FirstOrDefault();
// Users without a matching role may be deleted
if (targetUserRole != null)
{
// Determine the importance of the role of both
// the current user and the target user
var targetIndex = allowedRoles.IndexOf(targetUserRole);
var index = allowedRoles.IndexOf(role);
// Index==0 is SuperAdmin
// Otherwise index of role of targetuser must be higher
if (index > 0 && targetIndex <= index)
return;
}
}
// If we got here we can safely delete the user.
//TO DO: Deletion code
}
如果您想扩展层次结构,只需将声明值添加到适当位置的 allowedRoles 集合即可。
我想知道是否有更好的方法: 假设我有角色为 "SuperUser"、Admin、"Manager" 的用户, "Registered".
站点中注册的所有用户都具有 "Registered" 角色(例如 "Manager" 用户也具有 "Registered" 角色)。
现在我必须管理用户控制器上的 Delete 操作。我想达到的是:
- "Registered" 用户只能删除自己(即:从站点注册中删除)
- "Manager"用户可以删除自己和"Registered"用户,bug不能删除"SuperUser"和"Admin"
- "Admin"用户可以删除自己,"Managers"和"Registered"用户,bug不能删除"SuperUser"
- "SuperUser" 用户可以删除所有用户角色,包括超级用户。
所以我从以下代码开始:
[Authorize(Roles="Registered")]
public void Delete(int id)
{
string[] AllowedRoles = { "SuperAdmin", "Manager" };
if (_identity.FindFirst(ClaimTypes.UserData).Value == id.ToString())
{
//USER can delete himself!
//TO DO: Deletion code
}
else if (User.IsInAnyRole(AllowedRoles))
{
//CHECK IF I CAN DELETE THE GIVEN USER
}
}
我要做的是检查当前用户的每个角色和要删除的用户,但我真的不喜欢写很多"if"... 有更好的方法吗?
谢谢!
PS:不用担心 User.IsInAnyRole(这是一个自定义函数,用于验证用户是否属于指定角色之一。
与我所做的类似的场景,而不是将角色作为字符串然后作为枚举标志
[Flags]
public enum Permissions
{
None = 0,
Registered = 1 << 0,
SuperAdmin = 1 << 1,
Manager = 1 << 2,
// Etc...
}
然后,您可以使用此方法对用户确定允许哪些角色:
public bool IsInRole(Permissions roles)
{
var rolesToCheck = roles.GetFlags().Where(p => p != Permissions.None);
return rolesToCheck.Any(role => Roles.HasFlag(role));
}
和:
if(User.IsInRole(Permissions.SuperAdmin | Permissions.Manager)
{
// Do something
}
同样,您也可以通过创建 Authorize 属性的自定义扩展将其添加到属性中:
public class CustomAuthorize : ActionFilterAttribute, IActionFilter
{
public Permissions Roles { get; set; }
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
bool authorized = false;
var roleFlags = Roles.GetFlags();
if (!roleFlags.All(r => r == Permissions.None))
{
foreach (var role in roleFlags.Where(p => p != RolePermissions.None))
{
if (maritimeUser.Roles.HasFlag(role))
{
authorized = true;
}
}
}
if (Roles == Permissions.None)
{
// No roles set, so authorise = okay
return;
}
if (!authorized)
{
filterContext.Result =
new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Account"},
{"action", "Unauthorised"},
{"area", "" }
});
return;
}
}
并由
使用[CustomAuthorize(Roles = Permissions.SuperAdmin | Permissions.Manager)]
我想知道,是否有经过身份验证的用户不是 "registered"? IMO 这个角色不是必需的。如果您不同意,您可以修改下面的代码。
我不确定你的代码中的 _identity
和 User
是什么,但我假设 _identity
是用户管理器存储库,User
是当前的 httpcontext用户。我假设您需要 UserManager,因为如果不访问存储的声明(如 AspNetUserClaims),您将无法执行此测试。
请注意,我没有完全测试这段代码。
// using System.Collections.Generic;
// using System.Linq;
// using System.Security.Claims;
// This method is available for all authenticated users
[Authorize]
public void Delete(int id)
{
// Test if current user wants to delete itself
if (User.FindFirst(ClaimTypes.UserData).Value != id.ToString())
{
// Find all roles of the current user.
var roles = User.FindAll("role").Select(r => r.Value).ToList();
// A fixed list, ordered by importance
var allowedRoles = new List<string> { "SuperAdmin", "Admin", "Manager" };
// Highest role of the current user
var role = allowedRoles.Intersect(roles).FirstOrDefault();
// "Registered" user is not allowed to do anything with other users
if (role == null)
return;
// Get the rolename(s) of the target user. Something like this, where
// _identity is a repository (usermanager?) that has access to the database
var targetUserRoles = _identity.Where(u => u.Id == id).Roles().Select(r => r.Name).ToList();
//var targetUserRoles = new List<string> { "Admin" };
// Highest role of the target user, because you don't want to delete
// a user that is both Manager and SuperAdmin when you are Admin.
var targetUserRole = allowedRoles.Intersect(targetUserRoles).FirstOrDefault();
// Users without a matching role may be deleted
if (targetUserRole != null)
{
// Determine the importance of the role of both
// the current user and the target user
var targetIndex = allowedRoles.IndexOf(targetUserRole);
var index = allowedRoles.IndexOf(role);
// Index==0 is SuperAdmin
// Otherwise index of role of targetuser must be higher
if (index > 0 && targetIndex <= index)
return;
}
}
// If we got here we can safely delete the user.
//TO DO: Deletion code
}
如果您想扩展层次结构,只需将声明值添加到适当位置的 allowedRoles 集合即可。