代码之家  ›  专栏  ›  技术社区  ›  MikeWyatt

ASP.NET MVC中的非字符串角色名?

  •  26
  • MikeWyatt  · 技术社区  · 14 年前

    ASP.NET MVC对基于角色的安全性有很好的支持,但是字符串作为角色名的使用是令人恼火的,因为它们不能被强类型化为枚举。

    例如,我的应用程序中有一个“管理员”角色。“admin”字符串现在将存在于我的操作的authorize属性、我的母版页(用于隐藏选项卡)、我的数据库(用于定义每个用户可用的角色)以及我的代码或视图文件中需要为admin或非admin用户执行特殊逻辑的任何其他位置。

    除了编写自己的授权属性和过滤器之外,还有更好的解决方案可以处理枚举值集合吗?

    6 回复  |  直到 12 年前
        1
  •  21
  •   Matti Virkkunen    14 年前

    我通常使用一个带有一串字符串常量的类。这不是一个完美的解决方案,因为你需要记住在任何地方都坚持使用它,但至少它可以消除打字错误的可能性。

    static class Role {
        public const string Admin = "Admin";
    }
    
        2
  •  48
  •   Johnny Oshika    13 年前

    使用magic字符串可以灵活地在authorize属性中声明多个角色(例如,[authorize(roles=“admin,moderator”)],在使用强类型解决方案时,这些角色往往会丢失。但下面是如何在保持强类型化的同时保持这种灵活性。

    在使用位标志的枚举中定义角色:

    [Flags]
    public enum AppRole {
        Admin = 1,
        Moderator = 2,
        Editor = 4,
        Contributor = 8,
        User = 16
    }
    

    覆盖授权属性:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class MyAuthorizeAttribute : AuthorizeAttribute {
    
        public AppRole AppRole { get; set; }
    
        public override void OnAuthorization(AuthorizationContext filterContext) {
            if (AppRole != 0)
                Roles = AppRole.ToString();
    
            base.OnAuthorization(filterContext);
        }
    
    }
    

    现在,如果您可以像这样使用myauthorizeattribute:

    [MyAuthorize(AppRole = AppRole.Admin | AppRole.Moderator | AppRole.Editor)]
    public ActionResult Index() {
    
        return View();
    }
    

    以上操作将仅授权至少在所列角色之一(管理员、版主或编辑器)中的用户。该行为与MVC的默认authorizeattribute相同,但不包含magic字符串。

    如果使用此技术,以下是iprincipal上的一个扩展方法,它可能也很有用:

    public static class PrincipalExtensions {
    
        public static bool IsInRole(this IPrincipal user, AppRole appRole) {
    
            var roles = appRole.ToString().Split(',').Select(x => x.Trim());
            foreach (var role in roles) {
                if (user.IsInRole(role))
                    return true;
            }
    
            return false;
        }
    }
    

    您可以使用如下扩展方法:

    public ActionResult Index() {
        var allowed = User.IsInRole(AppRole.Admin | AppRole.Moderator | AppRole.Editor);
    
        if (!allowed) {
           // Do Something
        }
    
        return View();
    }
    
        3
  •  10
  •   JonoW    13 年前

    虽然它不使用枚举,但我使用了下面的解决方案,在这里我们将authorize过滤器子类化,以便在构造函数中接受长度可变的角色名称参数。使用这个方法以及在某个地方的const变量中声明的角色名,我们可以避免使用magic字符串:

    public class AuthorizeRolesAttribute : AuthorizeAttribute
    {
        public AuthorizeRolesAttribute(params string[] roles) : base()
        {
            Roles = string.Join(",", roles);
        }
    }
    
    public class MyController : Controller
    {
        private const string AdministratorRole = "Administrator";
        private const string AssistantRole = "Assistant";
    
        [AuthorizeRoles(AdministratorRole, AssistantRole)]
        public ActionResult AdminOrAssistant()
        {                        
            return View();
        }
    }
    

    (我在博客中更详细地介绍了这一点- http://tech-journals.com/jonow/2011/05/19/avoiding-magic-strings-in-asp-net-mvc-authorize-filters )

        4
  •  3
  •   Craig Stuntz    14 年前

    它不是 那个 难以定制 AuthorizeAttribute 按照你的建议。

    对其进行子类型化,为枚举类型添加自定义属性,然后调用 ToString() 在传递的值上。将其放到“常规角色”属性中。这只需要几行代码,以及 授权属性 仍然做所有真正的工作。

    +1马蒂也是,因为警察也是个不错的选择。

        5
  •  3
  •   Alex    12 年前

    我接受了Johnnyo的回答,但更改了枚举项以使用 DescriptionAttribute 指定角色的字符串值。如果您希望您的角色字符串与枚举名称不同,这很有用。

    枚举示例:

    [Flags]
    public enum AppRole
    {
        [Description("myRole_1")]
        RoleOne = 1,
        [Description("myRole_2")]
        RoleTwo = 2
    }
    

    扩展方法:

    public static bool IsInRole(this IPrincipal user, AppRole appRole)
    {
        var roles = new List<string>();
        foreach (var role in (AppRole[])Enum.GetValues(typeof(AppRole)))
            if ((appRole & role) != 0)
                roles.Add(role.ToDescription());
    
        return roles.Any(user.IsInRole);
    }
    

    自定义属性:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AppAuthorizeAttribute : AuthorizeAttribute
    {
        public AppRole AppRoles { get; set; }
    
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var roles = new List<string>();
            foreach (var role in (AppRole[])Enum.GetValues(typeof(AppRole)))
                if((AppRoles & role) != 0)
                    roles.Add(role.ToDescription());
    
            if (roles.Count > 0)
                Roles = string.Join(",", roles);
    
            base.OnAuthorization(filterContext);
        }
    }
    

    获取描述值的扩展方法:

    public static string ToDescription(this Enum value)
    {
        var da = (DescriptionAttribute[])
                 (value.GetType().GetField(value.ToString()))
                     .GetCustomAttributes(typeof (DescriptionAttribute), false);
        return da.Length > 0 ? da[0].Description : value.ToString();
    }
    
        6
  •  2
  •   ScottS    14 年前

    我使用了一个静态类来定义一组字符串常量,正如Matti建议的那样,在我当前的项目中,我使用下面的扩展方法和一个枚举。两种方法都很有效。

    public static class EnumerationExtension
    {
      public static string GetName(this Enum e)
      {
        return Enum.GetName(e.GetType(), e);
      }
    }