Permitir múltiples roles para acceder a la acción del controlador


274

En este momento, decoro un método como este para permitir que los "miembros" accedan a la acción de mi controlador

[Authorize(Roles="members")]

¿Cómo permito más de un rol? Por ejemplo, lo siguiente no funciona, pero muestra lo que estoy tratando de hacer (permitir el acceso de "miembros" y "administrador"):

[Authorize(Roles="members", "admin")] 

44
Por favor cambie la respuesta aceptada a esta pregunta. La persona con la respuesta actualmente aceptada la editó indicando que estaba equivocado.
Eric J.

Respuestas:


595

Otra opción es usar un solo filtro de autorización como lo publicó pero eliminar las citas internas.

[Authorize(Roles="members,admin")]

55
Funciona en MVC 5 también. +1
gkonuralp

44
Funciona en ASP.NET Core 1.0 (MVC 6) y Microsoft.AspNet.Identity v3. *
Soren

3
Esto está bien si solo tiene un controlador que necesita autorizar. Si tiene más de uno, está duplicando esas constantes de cadena (qué asco). Prefiero la clase estática que tiene los nombres de los roles. Mi mascota odio son las cadenas duplicadas ... muy mal.
robnick

1
@kraeg buenas noticias de que resolvió su problema. Ahora, considere eliminar sus comentarios, por favor
Pablo Claus

1
¿Por qué? Me llevó años resolver esto. Puede ser útil para alguien que experimente el mismo problema.
kraeg

129

Si desea utilizar roles personalizados, puede hacer esto:

CustomRoles clase:

public static class CustomRoles
{
    public const string Administrator = "Administrador";
    public const string User = "Usuario";
}

Uso

[Authorize(Roles = CustomRoles.Administrator +","+ CustomRoles.User)]

Si tiene pocos roles, tal vez pueda combinarlos (por claridad) de esta manera:

public static class CustomRoles
{
     public const string Administrator = "Administrador";
     public const string User = "Usuario";
     public const string AdministratorOrUser = Administrator + "," + User;  
}

Uso

[Authorize(Roles = CustomRoles.AdministratorOrUser)]

77
Esta sería una buena respuesta, si le explicara a las personas que no sabían qué hay detrás de CustomRoles.
James Skemp

1
@JamesSkemp ok, he extendido mi respuesta. Es muy simple. CustumRoles es una clase que creé que contiene algunas constantes, que se corresponde con mis roles de aplicación. Lo hice por varias razones: 1) Permite el uso de intellisense para evitar errores ortográficos 2) Para simplificar el mantenimiento. Si un cambio de rol, tengo que actualizar solo un lugar dentro de mi aplicación.
Pablo Claus

@Pabloker Alternativamente, puede crear una enumeración con un atributo Flags, por ejemplo, Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - Parte molesta es que esto requiere una conversión explícita
cstruter

Si tienes 39 roles?
Kiquenet

Creo que su problema pasa por el modelado de permisos más allá de lo que se puede hacer con .net
Pablo Claus

82

Una posible simplificación sería la subclase AuthorizeAttribute:

public class RolesAttribute : AuthorizeAttribute
{
    public RolesAttribute(params string[] roles)
    {
        Roles = String.Join(",", roles);
    }
}

Uso:

[Roles("members", "admin")]

Semánticamente es lo mismo que la respuesta de Jim Schmehil.


3
Esto no funcionó para mí, el usuario conectado pudo omitir el atributo incluso si el usuario no tenía ninguno de los roles.
Urielzen

9
Esta respuesta es mejor cuando utiliza constantes como sus valores: es decir, [Roles (Constantes.Admin, Constantes.Propietario)]
dalcam

3
esta es la mejor respuesta
IgorShch

18

Para MVC4, usando un Enum( UserRoles) con mis roles, uso una costumbre AuthorizeAttribute.

En mi acción controlada, hago:

[CustomAuthorize(UserRoles.Admin, UserRoles.User)]
public ActionResult ChangePassword()
{
    return View();
}

Y uso una costumbre AuthorizeAttribute como esa:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
    private string[] UserProfilesRequired { get; set; }

    public CustomAuthorize(params object[] userProfilesRequired)
    {
        if (userProfilesRequired.Any(p => p.GetType().BaseType != typeof(Enum)))
            throw new ArgumentException("userProfilesRequired");

        this.UserProfilesRequired = userProfilesRequired.Select(p => Enum.GetName(p.GetType(), p)).ToArray();
    }

    public override void OnAuthorization(AuthorizationContext context)
    {
        bool authorized = false;

        foreach (var role in this.UserProfilesRequired)
            if (HttpContext.Current.User.IsInRole(role))
            {
                authorized = true;
                break;
            }

        if (!authorized)
        {
            var url = new UrlHelper(context.RequestContext);
            var logonUrl = url.Action("Http", "Error", new { Id = 401, Area = "" });
            context.Result = new RedirectResult(logonUrl);

            return;
        }
    }
}

Esto es parte de FNHMVC modificado por Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/


1
Su método OnAuthorization requerirá que el usuario tenga todos los roles enumerados; ¿Fue intencional o te estás perdiendo un descanso en ese ciclo?
Tieson T.

@Tieson: Inspeccioné eso muy de cerca, definitivamente parece que se necesitaría un descanso en ese ciclo.
OcelotXL

@TiesonT. y @ madrush, agradezco su solución, realmente podría tener un descanso dentro del bucle. Cambiaré el código de arriba.
Bernardo Loureiro

La enumeración UserRoles es agradable. ¿Lo declara manualmente o se genera automáticamente en función del contenido de la base de datos?
Konrad Viltersten

@KonradViltersten Es manual, pero supongo que con la clase Reflection y Dynamic autogenerada se puede hacer
Bernardo Loureiro

3

Otra solución clara, puede usar constantes para mantener la convención y agregar múltiples atributos [Autorizar]. Mira esto:

public static class RolesConvention
{
    public const string Administrator = "Administrator";
    public const string Guest = "Guest";
}

Luego en el controlador:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller

14
Los Authorizeatributos múltiples emplean la semántica AND y requieren que se cumplan TODAS las condiciones (es decir, el usuario debe estar en los roles de administrador e invitado).
trousyt

3

Si te encuentras aplicando esos 2 roles a menudo, puedes envolverlos en su propia Autorización. Esto es realmente una extensión de la respuesta aceptada.

using System.Web.Mvc;

public class AuthorizeAdminOrMember : AuthorizeAttribute
{
    public AuthorizeAdminOrMember()
    {
        Roles = "members, admin";
    }
}

Y luego aplique su nueva autorización a la Acción. Creo que esto se ve más limpio y se lee fácilmente.

public class MyController : Controller
{
    [AuthorizeAdminOrMember]
    public ActionResult MyAction()
    {
        return null;
    }
}

1

Mejor código al agregar una subclase AuthorizeRole.cs

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    class AuthorizeRoleAttribute : AuthorizeAttribute
    {
        public AuthorizeRoleAttribute(params Rolenames[] roles)
        {
            this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
        }
        protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAuthenticated)
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Unauthorized" },
                  { "controller", "Home" },
                  { "area", "" }
                  }
              );
                //base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Login" },
                  { "controller", "Account" },
                  { "area", "" },
                  { "returnUrl", HttpContext.Current.Request.Url }
                  }
              );
            }
        }
    }

Como usar esto

[AuthorizeRole(Rolenames.Admin,Rolenames.Member)]

public ActionResult Index()
{
return View();
}

1

Usando AspNetCore 2.x, tienes que ir un poco diferente:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeRoleAttribute : AuthorizeAttribute
{
    public AuthorizeRoleAttribute(params YourEnum[] roles)
    {
        Policy = string.Join(",", roles.Select(r => r.GetDescription()));
    }
}

solo úsalo así:

[Authorize(YourEnum.Role1, YourEnum.Role2)]

-2
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk"), "application/vnd.android.package-archive" );

startActivity(promptInstall);

1
Las respuestas que incluyen el código deben tener al menos una descripción mínima que explique cómo funciona el código y por qué responde a la pregunta. Además, se debe mejorar el formato de la sección de código.
Roberto Caboni

¿Eh? @Orsit Moel, parece copiado en hilo equivocado ...
Cameron Forward
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.