Autorizar atributo con múltiples roles

Me gustaría agregar Autorización a un controlador, para múltiples Roles a la vez.

Normalmente, eso se vería así:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

Pero he guardado mis Roles en constantes, ya que pueden cambiar o extenderse en algún momento.

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

No puedo hacer esto, ya que la cadena debe conocerse en tiempo de compilación:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

¿Hay alguna manera de eludir el problema?

PODRÍA escribir una const que simplemente contenga "RoleA,RoleB,RoleC", pero no me gustan las cadenas mágicas y esta es una cadena mágica. Cambiar el nombre de un Rol y olvidarse de cambiar la cadena combinada sería un desastre.

Estoy usando MVC5. La identidad y el rol de ASP.NET se conocen en tiempo de compilación.

preguntado el 12 de junio de 14 a las 11:06

¿está utilizando public const string RoleA = "RoleA"; o como has escrito en cuestión? -

5 Respuestas

Intente crear un atributo de autorización personalizado como así.

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

Suponiendo que sus funciones serán las mismas para varios controladores, cree una clase auxiliar:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

Entonces úsalo así:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}

Respondido el 05 de enero de 15 a las 05:01

Esa es una idea digna de Mac Gyver ;) - cristian sauer

También me gusta mucho esta solución, especialmente porque puedo dejar que mi Rol sea una enumeración en lugar de una cadena. ¿Cuál sería un buen espacio de nombres y ubicación en la jerarquía del proyecto para colocar este atributo de autorización personalizado? - brillar

@SimonShine: creo que debería colocarlo en el proyecto web, por ejemplo, en la carpeta Atributos. Sin embargo, si tiene varios proyectos en una solución, deberá mover esto a otra biblioteca visible para cada proyecto. - MacGyver

No estoy seguro de lo que está pasando aquí, pero esto NO me ayudó, cualquier usuario, independientemente del rol, pudo acceder al método. - Urielzen

El mismo problema que @Urielzen, pero fue solucionado por la respuesta a continuación de Jerry Finegan (usando "System.Web.Mvc.AuthorizeAttribute y NO System.Web.Http.AuthorizeAttribute") - RJB

La forma mejor y más sencilla que encontré para resolver este problema es simplemente concatenar roles en el atributo Autorizar.

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

con CustomRole una clase con cadenas constantes como esta:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}

Respondido 31 Oct 18, 20:10

valioso; pero esto debería ser un comentario; no una respuesta - gato fantasma

Tanto su respuesta como la respuesta aceptada activarán la autorización si se implementan correctamente (estoy usando la aceptada en una aplicación web de producción). Proponer una edición para eliminar los comentarios sobre la respuesta aceptada. - Eric Eskildsen

Si los roles fueran enumeraciones, puede usar algo como [Authorize(Roles = nameof(UserRoleEnum.User) + "," + nameof(UserRoleEnum.Admin))] - Varun

Asegúrese de derivar su clase de atributo personalizado System.Web.Mvc.AuthorizeAttribute y no System.Web.Http.AuthorizeAttribute.

Tuve el mismo problema. Una vez que lo cambié, todo funcionó.

También es posible que desee agregar lo siguiente a su clase de atributo personalizado:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 

Respondido 07 ago 16, 05:08

Acabo de probar esto y encontré una referencia a la biblioteca. System.Web.Http.AuthorizeAttribute EN LUGAR DE System.Web.Mvc.AuthorizeAttribute - fraser jordan

Lo que hice es la respuesta en @Tieson

Modifico un poco su respuesta. En lugar de cadena. Únase, ¿por qué no convertirlo en lista?

Esta es mi respuesta:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

Y luego verifique si el rol es válido anulando OnAuthorization

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);

                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

Y ahí lo tienes, ahora está validando si el rol está autorizado para acceder al recurso.

respondido 08 mar '18, 01:03

Siento que un atributo de autorización personalizado es excesivo para este problema a menos que tenga una gran cantidad de roles.

Dado que la cadena debe conocerse en el momento de la compilación, ¿por qué no crear una clase de función estática que contenga cadenas públicas de las funciones que ha definido y luego agregar cadenas separadas por comas con ciertas funciones que desee autorizar?

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

Y luego puede usar el Atributo de autorización como tal en la Clase de controlador o el Método de controlador (o ambos):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}

Respondido 12 ago 17, 22:08

Este ejemplo no funciona, o al menos no de la manera que podrías pensar. Por ejemplo, mientras que la novela el ADMIN_OR_VIEWER papel en la acción es redundante porque no se le permitirá llegar a la Create método si aún no tiene el ADMIN role. En este caso VIEWER nunca podrá invocar Create método. - Juan Leidegren

Esta solución tampoco es escalable. Habrá un punto en el que tengas demasiados roles con diferentes acciones y no deberías crear todas las combinaciones: edulopez

@JohnLeidegren esto es incorrecto. El AuthorizeAttribute de nivel de acción anula el AuthorizeAttribute de nivel de controlador, de lo contrario, no podría tener acciones decoradas con AllowAnonymousAttribute en un controlador con un AuthorizeAttribute - Tom pelusa

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.