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

Posted

技术标签:

【中文标题】ASP.NET MVC 中的非字符串角色名称?【英文标题】:Non-string role names in ASP.NET MVC? 【发布时间】:2011-02-19 04:24:32 【问题描述】:

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

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

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

【问题讨论】:

【参考方案1】:

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

static class Role 
    public const string Admin = "Admin";

【讨论】:

我选择了这个解决方案,因为它很简单。代码更改很少,因为我只需要用常量引用替换硬编码的字符串。【参考方案2】:

按照您建议的方式自定义AuthorizeAttribute 并不难

子类型,为您的枚举类型添加自定义属性,并在传递的值上调用ToString()。把它放在常规角色属性中。这应该只需要几行代码,AuthorizeAttribute 仍然可以完成所有实际工作。

Matti 也是 +1,因为 const 也是一个不错的选择。

【讨论】:

【参考方案3】:

按照 Matti 的建议,我使用了一个定义了一堆字符串常量的静态类,在我当前的项目中,我使用了下面的带有枚举的扩展方法。这两种方法都非常有效。

public static class EnumerationExtension

  public static string GetName(this Enum e)
  
    return Enum.GetName(e.GetType(), e);
  

【讨论】:

【参考方案4】:

使用魔术字符串可以让您灵活地在 Authorize 属性中声明多个角色(例如 [Authorize(Roles = "Admin, Moderator")],当您使用强类型解决方案时,您往往会丢失这些角色。但这里是您的方法可以保持这种灵活性,同时仍然让所有内容都被强类型化。

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

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

覆盖 AuthorizeAttribute:

[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 相同,只是没有魔术字符串。

如果您使用这种技术,这里有一个 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();

【讨论】:

我非常喜欢这种方法,易于实施和维护 @Jammer,枚举值不一定需要与数据库 ID 匹配。它们可以是独立的,并且仍然可以正常工作。【参考方案5】:

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

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)

【讨论】:

您将如何进一步自定义它使其成为函数或委托?即 user=> user.Role == AssistantRole || user.Role == BigGuy ...有些动作可能需要一个角色而不是另一个,有些可能需要2个角色或第3个角色,我希望我清楚??? :)【参考方案6】:

我接受了 JohnnyO 的回复,但将枚举项更改为使用DescriptionAttribute 来指定角色的字符串值。如果您希望角色字符串与 Enum 名称不同,这会派上用场。

枚举示例:

[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();

【讨论】:

以上是关于ASP.NET MVC 中的非字符串角色名称?的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net Mvc 使用用户角色编辑用户配置文件

ASP.NET MVC 表单身份验证 + 授权属性 + 简单角色

带有参数的 ASP.NET MVC 授权属性

ASP.NET MVC:显示用户角色

ASP.NET MVC、Telerik Kendo、jQuery 验证中的非标准日期格式

获取 ASP.NET MVC 中的角色列表