paginacion net mvc filtros filtro datos busqueda buscar buscador asp asp.net-mvc roles

asp.net-mvc - net - paginacion mvc razor



¿Nombres de rol no de cadena en ASP.NET MVC? (6)

ASP.NET MVC tiene un buen soporte para la seguridad basada en roles, pero el uso de cadenas como nombres de roles es enloquecedor, simplemente porque no se pueden tipear fuertemente como enumeración.

Por ejemplo, tengo un rol de "administrador" en mi aplicación. La cadena "Admin" ahora existirá en el atributo Autorizar de mi acción, en mi página maestra (para ocultar una pestaña), en mi base de datos (para definir los roles disponibles para cada usuario) y en cualquier otro lugar en mi código o vista archivos donde necesito realizar una lógica especial para usuarios administradores o no administradores.

¿Existe una solución mejor, aparte de escribir mi propio atributo de autorización y filtro, que quizás trate con una colección de valores de enumeración?


Aunque no usa enumeraciones, he usado la solución a continuación, donde subdividimos el filtro Autorizar para tomar argumentos de nombre de rol de longitud variable en el constructor. Usando esto junto con los nombres de roles declarados en variables const en algún lugar, evitamos cadenas mágicas:

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

(Hice un blog sobre esto en un poco más de detalle - http://tech-journals.com/jonow/2011/05/19/avoiding-magic-strings-in-asp-net-mvc-authorize-filters )


El uso de cadenas mágicas le brinda la flexibilidad de declarar múltiples roles en el atributo Autorizar (por ejemplo, [Autorizar (Roles = "Admin, Moderador")] que tiende a perder a medida que avanza hacia una solución fuertemente tipada. Pero así es cómo puede mantener esto. Flexibilidad sin dejar de escribir todo fuertemente.

Defina sus roles en una enumeración que usa banderas de bits:

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

Anular AutorizaciónAtributo:

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

Ahora si puede usar MyAuthorizeAttribute de esta manera:

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

La acción anterior solo autorizará a los usuarios que estén en al menos uno de los roles enumerados (administrador, moderador o editor). El comportamiento es el mismo que el AuthorizeAttribute predeterminado de MVC, excepto sin las cadenas mágicas.

Si utiliza esta técnica, aquí hay un método de extensión en IPrincipal que también puede ser útil:

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

Puedes usar este método de extensión así:

public ActionResult Index() { var allowed = User.IsInRole(AppRole.Admin | AppRole.Moderator | AppRole.Editor); if (!allowed) { // Do Something } return View(); }


He usado una clase estática que define un grupo de constantes de cadena como lo sugiere Matti y en mi proyecto actual, uso el método de extensión a continuación con una enumeración. Ambos enfoques funcionan muy bien.

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


No es tan difícil personalizar AuthorizeAttribute de la forma que sugieres.

Subtípelo, agregue una propiedad personalizada para su tipo de enumeración y llame a ToString() sobre el valor pasado. Pon eso en la propiedad de roles regulares. Esto debería tomar solo unas pocas líneas de código, y AuthorizeAttribute todavía hace todo el trabajo real.

+1 para Matti, también, ya que consts también es una buena opción.


Tomé la respuesta de JohnnyO pero modifiqué los elementos de la enumeración para usar el atributo DescriptionAttribute para especificar el valor de cadena para el rol. Esto es útil si desea que su cadena de rol sea diferente del nombre Enum.

El ejemplo de enumeración:

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

El método de extensión:

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

El atributo personalizado:

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

Método de extensión para obtener el valor de descripción:

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


Usualmente uso una clase con un montón de constantes de cuerdas. No es una solución perfecta, ya que debes acordarte de utilizarla en todas partes, pero al menos elimina la posibilidad de errores tipográficos.

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