【问题标题】:asp.net mvc decorate [Authorize()] with multiple enumsasp.net mvc 用多个枚举装饰 [Authorize()]
【发布时间】:2010-11-12 00:13:19
【问题描述】:

我有一个控制器,我希望两个角色能够访问它。 1 位管理员或 2 位版主

我知道你可以做 [Authorize(Roles="admin, moderators")] 但我在枚举中有我的角色。使用枚举我只能授权一个角色。我不知道如何授权两个。

我尝试过 [Authorize(Roles=MyEnum.Admin, MyEnum.Moderator)] 之类的方法,但无法编译。

曾经有人建议过:

 [Authorize(Roles=MyEnum.Admin)]
 [Authorize(MyEnum.Moderator)]
 public ActionResult myAction()
 {
 }

但它不能用作 OR。我认为在这种情况下,用户必须是两个角色的一部分。我是否忽略了一些语法?或者这是我必须推出自己的自定义授权的情况?

【问题讨论】:

  • 有趣。即使在枚举中只有一个角色,你是如何让它工作的? MyEnum.Admin 是否返回字符串?我正在尝试和你做同样的事情,但我遇到了几个问题: - 我无法设置字符串类型的枚举。 - 我不能在枚举上调用 ToString() 例如[Authorize(Roles=MyEnum.Admin.ToString())] 上面的两个例子都给了我编译器错误。如果你能告诉你如何让它工作,那将不胜感激。谢谢。
  • @JohnnyO - 我也有同样的问题,你有没有发现我们做错了什么? @codette - 你能给我们小费吗?
  • 很抱歉,我从未采用过这些解决方案。所以我改变了我的代码,只需要检查一个角色。他们的角色越高,他们能做的就越多。例如,“普通”用户可以做一些事情。 “版主”可以做“普通”用户可以做的所有事情,甚至更多。 “管理员”可以做“普通”用户和“版主”可以做的一切,甚至更多。

标签: asp.net-mvc authorization roles


【解决方案1】:

尝试像这样使用位或运算符:

[Authorize(Roles= MyEnum.Admin | MyEnum.Moderator)]
public ActionResult myAction()
{
}

如果这不起作用,您可以自己滚动。我目前只是在我的项目上做了这个。这是我所做的:

public class AuthWhereRole : AuthorizeAttribute
{
    /// <summary>
    /// Add the allowed roles to this property.
    /// </summary>
    public UserRole Is;

    /// <summary>
    /// Checks to see if the user is authenticated and has the
    /// correct role to access a particular view.
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
            throw new ArgumentNullException("httpContext");

        // Make sure the user is authenticated.
        if (!httpContext.User.Identity.IsAuthenticated)
            return false;

        UserRole role = someUser.Role; // Load the user's role here

        // Perform a bitwise operation to see if the user's role
        // is in the passed in role values.
        if (Is != 0 && ((Is & role) != role))
            return false;

        return true;
    }
}

// Example Use
[AuthWhereRole(Is=MyEnum.Admin|MyEnum.Newbie)]
public ActionResult Test() {}

另外,请确保向您的枚举添加一个标志属性,并确保它们的值都从 1 及以上开始。像这样:

[Flags]
public enum Roles
{
    Admin = 1,
    Moderator = 1 << 1,
    Newbie = 1 << 2
    etc...
}

左移位给出值 1、2、4、8、16 等等。

嗯,我希望这会有所帮助。

【讨论】:

  • 完美!正是我需要的!谢谢。
  • 如果用户可能属于多个角色怎么办?
  • 我的数据库中需要什么值才能选择管理员、版主或新手? (db.users.role = 1 ; 2 ; 4 ; 8... ?
  • 使用此解决方案,您可能只有 32 个角色,它们可能会被 ORed 在一起。可能是限制性的(对我来说是)。更好的解决方案是来自@ZaidMasud 的解决方案。
【解决方案2】:

或者你可以像这样连接:

[Authorize(Roles = Common.Lookup.Item.SecurityRole.Administrator + "," + Common.Lookup.Item.SecurityRole.Intake)]

【讨论】:

    【解决方案3】:

    添加到 CalebHC 的代码并回答 ssmith 关于处理具有多个角色的用户的问题...

    我们的自定义安全主体返回一个字符串数组,表示用户所在的所有组/角色。因此,首先我们必须转换数组中与枚举中的项目匹配的所有字符串。最后,我们寻找任何匹配 - 如果是,则用户被授权。

    请注意,我们还将未经授权的用户重定向到自定义的“NotAuthorized”视图。

    整个班级是这样的:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// Add the allowed roles to this property.
        /// </summary>
        public Roles Is { get; set; }
    
        /// <summary>
        /// Checks to see if the user is authenticated and has the
        /// correct role to access a particular view.
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException("httpContext");
    
            if (!httpContext.User.Identity.IsAuthenticated)
                return false;
    
            var iCustomPrincipal = (ICustomPrincipal) httpContext.User;
    
            var roles = iCustomPrincipal.CustomIdentity
                            .GetGroups()
                            .Select(s => Enum.Parse(typeof (Roles), s))
                            .ToArray();
    
            if (Is != 0 && !roles.Cast<Roles>().Any(role => ((Is & role) == role)))
            {
                return false;
            }
    
            return true;
        }
    
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext == null)
                throw new ArgumentNullException("filterContext");
    
            filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
        } 
    }
    

    【讨论】:

      【解决方案4】:

      这是一个简单且优雅的解决方案,它允许您简单地使用以下语法:

      [AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]
      

      创建自己的属性时,在构造函数中使用 params 关键字:

      public class AuthorizeRoles : AuthorizeAttribute
      {
          public AuthorizeRoles(params MyEnum[] roles)
          {
              ...
          }
          protected override bool AuthorizeCore(HttpContextBase httpContext)
          {
              ...
          }
      }
      

      这将允许您按如下方式使用该属性:

      [AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]
      public ActionResult myAction()
      {
      }
      

      【讨论】:

      • 我相信,这应该是一个可接受的解决方案,因为它有效,并且您在接受的答案中最多不限于 32 个角色。谢谢你。
      • 只是对这里发生的事情的提示(好吧 - 它愚弄了我 5 分钟!)。您正在创建 AuthorizeRoles 对象,然后它将调用 AuthorizeCore 以确定是否授予访问权限。这样的事情可能会有所帮助。 stackoverflow.com/questions/1148312/…
      • @zoka 同意。这个解决方案比我的更好,更灵活。 :)
      【解决方案5】:

      试试

      public class CustomAuthorize : AuthorizeAttribute
      {
          public enum Role
          {
              DomainName_My_Group_Name,
              DomainName_My_Other_Group_Name
          }
      
          public CustomAuthorize(params Role[] DomainRoles)
          {
              foreach (var domainRole in DomainRoles)
              {
                  var domain = domainRole.ToString().Split('_')[0] + "_";
                  var role = domainRole.ToString().Replace(domain, "").Replace("_", " ");
                  domain=domain.Replace("_", "\\");
                  Roles += ", " + domain + role;
              }
              Roles = Roles.Substring(2);
          }       
      }
      
      public class HomeController : Controller
      {
          [CustomAuthorize(Role.DomainName_My_Group_Name, Role.DomainName_My_Other_Group_Name)]
          public ActionResult Index()
          {
              return View();
          }
      }
      

      【讨论】:

        【解决方案6】:

        这是我的版本,基于 @CalebHC 和 @Lee Harold 的回答。

        我遵循了在属性中使用命名参数的风格,并覆盖了基类Roles属性。

        @CalebHC 的答案使用了一个新的Is 属性,我认为这是不必要的,因为AuthorizeCore() 被覆盖(在基类中使用角色),所以使用我们自己的Roles 也是有意义的。通过使用我们自己的Roles,我们可以在控制器上写入Roles = Roles.Admin,它遵循其他.Net 属性的样式。

        我使用了CustomAuthorizeAttribute 的两个构造函数来显示传入的真实活动目录组名称。在生产中,我使用参数化构造函数来避免类中的魔术字符串:组名称在@987654331 期间从web.config 中提取@ 并在创建时使用 DI 工具传入。

        您的Views\Shared 文件夹中需要NotAuthorized.cshtml 或类似名称,否则未经授权的用户将收到错误屏幕。

        这是基类AuthorizationAttribute.cs的代码。

        控制器:

        public ActionResult Index()
        {
          return this.View();
        }
        
        [CustomAuthorize(Roles = Roles.Admin)]
        public ActionResult About()
        {
          return this.View();
        }
        

        自定义授权属性:

        // The left bit shifting gives the values 1, 2, 4, 8, 16 and so on.
        [Flags]
        public enum Roles
        {
          Admin = 1,
          User = 1 << 1    
        }
        
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
        public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
          private readonly string adminGroupName;
        
          private readonly string userGroupName;
        
          public CustomAuthorizeAttribute() : this("Domain Admins", "Domain Users")
          {      
          }
        
          private CustomAuthorizeAttribute(string adminGroupName, string userGroupName)
          {
            this.adminGroupName = adminGroupName;
            this.userGroupName = userGroupName;
          }
        
          /// <summary>
          /// Gets or sets the allowed roles.
          /// </summary>
          public new Roles Roles { get; set; }
        
          /// <summary>
          /// Checks to see if the user is authenticated and has the
          /// correct role to access a particular view.
          /// </summary>
          /// <param name="httpContext">The HTTP context.</param>
          /// <returns>[True] if the user is authenticated and has the correct role</returns>
          /// <remarks>
          /// This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
          /// </remarks>
          protected override bool AuthorizeCore(HttpContextBase httpContext)
          {
            if (httpContext == null)
            {
              throw new ArgumentNullException("httpContext");
            }
        
            if (!httpContext.User.Identity.IsAuthenticated)
            {
              return false;
            }
        
            var usersRoles = this.GetUsersRoles(httpContext.User);
        
            return this.Roles == 0 || usersRoles.Any(role => (this.Roles & role) == role);
          }
        
          protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
          {
            if (filterContext == null)
            {
              throw new ArgumentNullException("filterContext");
            }
        
            filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
          }
        
          private IEnumerable<Roles> GetUsersRoles(IPrincipal principal)
          {
            var roles = new List<Roles>();
        
            if (principal.IsInRole(this.adminGroupName))
            {
              roles.Add(Roles.Admin);
            }
        
            if (principal.IsInRole(this.userGroupName))
            {
              roles.Add(Roles.User);
            }
        
            return roles;
          }    
        }
        

        【讨论】:

        • 如果有人真的试图通过构造函数注入来使用这个属性,它不会起作用,因为属性是通过反射 API 创建的 - 请参阅post 了解更多信息。上面发布的代码是在使用 DI 之前的第一次剪辑。
        • 最后,我决定不使用 DI,因为我使用的库 (Autofac + Autofac.MVC4) 仅支持我在 beta 版本中尝试实现的功能,我不想用。这是有关如何使用Autofac.MVC4 beta 执行此类操作的链接。我的最终版本取消了构造函数参数,并将adminGroupNameuserGroupName 转换为const
        【解决方案7】:

        我在这里结合了一些解决方案来创建我个人最喜欢的解决方案。我的自定义属性只是将数据更改为 SimpleMembership 期望的形式,并让它处理其他所有内容。

        我的角色枚举:

        public enum MyRoles
        {
            Admin,
            User,
        }
        

        创建角色:

        public static void CreateDefaultRoles()
        {
            foreach (var role in Enum.GetNames(typeof(MyRoles)))
            {
               if (!Roles.RoleExists(role))
               {
                    Roles.CreateRole(role);
                }
            }
        }
        

        自定义属性:

        public class AuthorizeRolesAttribute : AuthorizeAttribute
        {
            public AuthorizeRolesAttribute(params MyRoles[] allowedRoles)
            {
                var allowedRolesAsStrings = allowedRoles.Select(x => Enum.GetName(typeof(MyRoles), x));
                Roles = string.Join(",", allowedRolesAsStrings);
            }
        }
        

        这样使用:

        [AuthorizeRoles(MyRoles.Admin, MyRoles.User)]
        public ActionResult MyAction()
        {
            return View();
        }
        

        【讨论】:

          猜你喜欢
          • 2022-11-28
          • 1970-01-01
          • 2011-09-02
          • 1970-01-01
          • 2014-12-21
          • 1970-01-01
          • 1970-01-01
          • 2021-12-29
          • 2023-03-30
          相关资源
          最近更新 更多