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")] 
codette
fuente
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")]
Jim Schmehil
fuente
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)]
Pablo Claus
fuente
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.

Mihkel Müür
fuente
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/

Bernardo Loureiro
fuente
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
Renê R. Silva
fuente
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;
    }
}
GER
fuente
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();
}
Kinzzy Goel
fuente
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)]
Daniel DirtyNative Martin
fuente
-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);
Orsit Moel
fuente
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