Aprende en casa Ir a KODOTI
Aprende en casa KODOTI

Roles y Permisos personalizados con ASP.NET MVC

En esta entrada les voy a explicar la lógica que uso para manejar el tema de roles y permisos en ASP.NET MVC.

Rodríguez Patiño, Eduardo
Rodríguez Patiño, Eduardo
2020-07-03 | 51,285 lecturas

En esta entrada voy a explicar la lógica que yo uso para implementar roles y permisos con ASP.NET MVC, para esto he creado 4 tablas.

Nuestra base de datos

  • Usuario: está tabla maneja todo los usuarios de nuestro sistema, y tiene un campo que se llama Rol_id, el cual hace referencia a la tabla siguiente.
  • Rol: está maneja los roles que va a implementar nuestro sistemas, ejm: Super Administrador, Administrador, Moderador, Super Usuario, etc.
  • Permiso: está tabla maneja todo los permisos que se nuestro negocio requiera, ejm: ¿Puede crear un nuevo alumno?, ¿Puede visualizar un alumno?, ¿Puede agregar una nota a un alumno?.
  • PermisoDenegadoPorRol: está tabla funciona al revés, ya que lo normal sería asignarle permisos a un usuario, para mí, un usuario tiene permiso para todo, entonces es más fácil ir agregando los permisos a los cuales no puede implementar. Esto lo hago porque es más facil, del caso contrario tendríamos que agregar todos los permisos disponibles e ir quitandolos.

Nuestro capa de modelo

En esta capa lo que he heco es crear una clase llamada FrontUser, esta clase nos permite saber que usuario esta autenticado en nuestro sistema y además implementa un método para saber si tiene permiso o no.

public class FrontUser
{
    public static bool TienePermiso(RolesPermisos valor)
    {
        var usuario = FrontUser.Get();
        return !usuario.Rol.Permiso.Where(x => x.PermisoID == valor)
                           .Any();
    }

    public static Usuario Get()
    {
        return new Usuario().Obtener(SessionHelper.GetUser());
    }
}

Si se dieron cuenta, el método TienePermiso hace lo inverso, si es verdadero retorna falso, del caso contrario true. Además, recibe como parámetro un Enum. ¿Por qué hago esto?, porque es una buena práctica tener los valores seteados, en vez de pasasr un número, esto hace que el código no sea mantenible. Entonces, lo que hay que hacer es pasar nuestros permisos a Enums.

public enum RolesPermisos
{
    #region Alumnos
    Alumno_Puede_Crear_Nuevo_Registro = 2,
    Alumno_Puede_Eliminar_Registro = 3,
    Alumno_Puede_Visualizar_Un_Alumno = 4,
    #endregion
}

Si, es más tedioso, ya que para cada nuevo permiso hay que agregarlo como un nuevo Enum, pero es más facil llamar a los Enums que hacerlo por números.

PD: la clase Permiso tiene una propiedad PermisoID, está cambiamos el tipo a RolesPermisos para que haga referencia al Enum. Al fín y al cabo, un Enum es un entero.

public partial class Permiso
{
    public Permiso()
    {
        Rol = new List<Rol>();
    }

    public <strong>RolesPermisos </strong>PermisoID { get; set; }

    [Required]
    [StringLength(20)]
    public string Modulo { get; set; }

    [Required]
    [StringLength(100)]
    public string Descripcion { get; set; }

    public virtual ICollection<Rol> Rol { get; set; }
}

Nuestro capa de web

Ahora que ya tenemos los permisos y roles, lo que hay que hacer es decorar las acciones con los filtros (CustomAttributes) que vamos a implementar, este ya sabe que tiene que buscar un permiso X del usuario que se autenticado en el sistema.

public class PermisoAttribute : ActionFilterAttribute
{
    public RolesPermisos Permiso { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        if (!FrontUser.TienePermiso(this.Permiso))
        {
            filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
            {
                controller = "Home",
                action = "Index"
            }));
        }
    }
}

Si no tiene el permiso, lo redireccionamos a la página de principal.

¿Como implementamos los permisos?

Como mencioné, hay que decorar las acciones de nuestro controlador. Miren el siguiente ejemplo, donde valido si el usuario autenticado puede visualizar un alumno.

[PermisoAttribute(Permiso = RolesPermisos.Alumno_Puede_Visualizar_Un_Alumno)]
public ActionResult Ver(int id)
{
    return View(alumno.Obtener(id));
}

¿Ven que es más facil pasar un Enum que un entero?. En el futuro, con tan solo leer el Enum ya sabemos a que permiso se refiere.

¿Como escondo botones, enlaces mediante Roles y Permisos?

De acuerdo, al filtrar las acciones de nuestros controladores denegamos el acceso por la URL, pero también sería bueno que lo hagamos visualmente. Para eso llamamos a la clase FrontUser directamente y preguntamos si tiene el permiso. En este ejemplo, estoy validando si el usuario puede crear un nuevo alumno o no.

<h2 class="page-header">
    @if(FrontUser.TienePermiso(RolesPermisos.Alumno_Puede_Crear_Nuevo_Registro))
    {
        <a href="~/home/crud" class="btn btn-default pull-right">Nuevo alumno</a>
    }
    Alumnos
</h2>

Espero que te haya gustado la entrada, si tienes dudas comentala y te voy a responder, pero por favor, ten paciencia, soy humano, trabajo, estoy conviviendo y estudio, así que puede ser que me demore en responder.


Estudia con nosotros

🚀 Mejora tus oportunidades laborales