Non-string role names in ASP.NET MVC?

£可爱£侵袭症+ 提交于 2019-11-27 05:08:37

问题


ASP.NET MVC has good support for role-based security, but the usage of strings as role names is maddening, simply because they cannot be strongly-typed as enumerations.

For example, I have an "Admin" role in my app. The "Admin" string will now exist in the Authorize attribute of my action, in my master page (for hiding a tab), in my database (for defining the roles available to each user), and any other place in my code or view files where I need to perform special logic for admin or non-admin users.

Is there a better solution, short of writing my own authorization attribute and filter, that would perhaps deal with a collection of enumeration values?


回答1:


I usually use a class with a bunch of string constants. It's not a perfect solution, since you need to remember to stick to using it everywhere, but at least it gets rid of the possibility of typos.

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



回答2:


Using magic strings gives you the flexibility to declare multiple roles in the Authorize attribute (e.g. [Authorize(Roles = "Admin, Moderator")] which you tend to lose as you go to a strongly typed solution. But here's how you can maintain this flexibility while still getting everything strongly typed.

Define your roles in an enum that uses bit flags:

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

Override 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);
    }

}

Now if you can use MyAuthorizeAttribute like this:

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

    return View();
}

The above action will only authorize users that are in at least one of the roles listed (Admin, Moderator, or Editor). The behavior is the same as MVC's default AuthorizeAttribute, except without the magic strings.

If you use this technique, here's an extension method on IPrincipal that may also be useful:

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;
    }
}

You can use this extension method like this:

public ActionResult Index() {
    var allowed = User.IsInRole(AppRole.Admin | AppRole.Moderator | AppRole.Editor);

    if (!allowed) {
       // Do Something
    }

    return View();
}



回答3:


Although it doesn't use enums, I've used the solution below, where we sub-class the Authorize filter to take in variable length role name arguments in the constructor. Using this together with role names declared in const variables somewhere, we avoid magic strings:

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

(I blogged about this in a little bit more detail - http://tech-journals.com/jonow/2011/05/19/avoiding-magic-strings-in-asp-net-mvc-authorize-filters)




回答4:


It's not that hard to customize AuthorizeAttribute in the way you suggest.

Subtype it, add a custom property for your enum type, and call ToString() on the passed value. Put that in the regular roles property. This should take just a few lines of code, and AuthorizeAttribute still does all the real work.

+1 for Matti, too, since consts are also a good choice.




回答5:


I took JohnnyO's response but changed the enumeration items to use the DescriptionAttribute to specify the string value for the role. This comes in handy if you want your role string to be different from the Enum name.

The enum example:

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

The extension method:

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);
}

The custom attribute:

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

Extension method to get the description value:

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:


I have used a static class defining a bunch of string constants as suggested by Matti and on my current project I use the below extension method with an enum. Both approaches work very well.

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


来源:https://stackoverflow.com/questions/2828444/non-string-role-names-in-asp-net-mvc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!