【问题标题】:.NET MVC: Restrict action based on Role level.NET MVC:根据角色级别限制操作
【发布时间】:2020-01-21 06:56:11
【问题描述】:

我想知道是否有更好的方法: 假设我有角色为 “SuperUser”Admin“Manager”“Registered” 的用户。

在站点中注册的所有用户都具有“已注册”角色(例如“经理”用户也具有“已注册”角色)。

现在我要管理用户控制器上的 Delete 操作。我想达到的是:

  • “已注册”用户只能删除自己(es:从站点注册中删除)
  • “经理”用户可以删除自己和“注册”用户,错误不能删除“超级用户”和“管理员”
  • “Admin”用户可以删除自己,“Managers”和“Registered”用户,错误不能删除“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
            }

        }

我要做的是检查当前用户的每个角色以及要删除的用户,但我真的不喜欢写很多“如果”...... 有没有更好的方法?

谢谢!

PS:不要担心 User.IsInAnyRole(它是一个自定义函数,用于验证用户是否处于指定角色之一。

【问题讨论】:

    标签: c# asp.net-mvc authorization roles


    【解决方案1】:

    与我所做的类似的场景,而不是将角色作为字符串然后作为枚举标志

    [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)]
    

    【讨论】:

      【解决方案2】:

      我想知道,是否存在未“注册”的经过身份验证的用户? IMO 这个角色是没有必要的。如果您不同意,您可以修改下面的代码。

      我不确定您的代码中的 _identityUser 是什么,但我假设 _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 集合的适当位置即可。

      【讨论】:

      • 是的,您假定用户和 _identity 是正确的。 :) 出于某些原因,我需要一个“注册”角色和一个“访客”角色,但这并不重要。你的例子很好!谢谢。
      猜你喜欢
      • 2011-01-26
      • 2011-09-28
      • 1970-01-01
      • 1970-01-01
      • 2019-07-29
      • 2014-06-16
      • 2014-12-04
      • 2014-02-26
      • 2013-06-28
      相关资源
      最近更新 更多