Consentire a più ruoli di accedere all'azione del controller


274

In questo momento decoro un metodo come questo per consentire ai "membri" di accedere all'azione del mio controller

[Authorize(Roles="members")]

Come posso consentire più di un ruolo? Ad esempio, quanto segue non funziona ma mostra cosa sto cercando di fare (consenti l'accesso "membri" e "admin"):

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

4
Modifica la risposta accettata a questa domanda. La persona con la risposta attualmente accettata l'ha modificata indicando che aveva torto.
Eric J.

Risposte:


595

Un'altra opzione è quella di utilizzare un singolo filtro di autorizzazione durante la pubblicazione ma rimuovere le citazioni interne.

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

5
Funziona anche in MVC 5. +1
gkonuralp

4
Funziona in ASP.NET Core 1.0 (MVC 6) e Microsoft.AspNet.Identity v3. *
Soren,

3
Questo va bene se hai un solo controller che devi autorizzare. Se ne hai più di una, stai duplicando quelle costanti di stringa (yuck). Preferisco di gran lunga la classe statica che ha i nomi dei ruoli. Il mio odio da compagnia è una stringa duplicata ... così terribile.
robnick,

1
@kraeg una buona notizia che hai risolto il tuo problema. Ora, considera di eliminare i tuoi commenti, per favore
Pablo Claus,

1
Perché? Mi ci sono voluti anni per risolverlo. Potrebbe essere utile per qualcun altro con lo stesso problema.
kraeg,

129

Se vuoi usare ruoli personalizzati, puoi farlo:

CustomRoles classe:

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

uso

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

Se hai pochi ruoli, forse puoi combinarli (per chiarezza) in questo modo:

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)]

7
Questa sarebbe una buona risposta, se spiegassi a persone che non sapevano cosa c'è dietro CustomRoles.
James Skemp,

1
@JamesSkemp ok, ho esteso la mia risposta. È molto semplice. CustumRoles è una classe che ho creato che contiene alcune costanti, che corrisponde ai ruoli della mia applicazione. L'ho fatto per alcuni motivi: 1) Permette l'uso dell'intellisense per evitare errori di ortografia 2) Semplificare la manutenzione. Se un ruolo cambia, devo aggiornare solo un posto all'interno della mia applicazione.
Pablo Claus,

@Pabloker In alternativa puoi creare un enum con un attributo Flags, ad esempio Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - la parte fastidiosa è che ciò richiede una conversione esplicita
cstruter

Se hai 39 ruoli?
Kiquenet,

Penso che il tuo problema passi attraverso la modellizzazione dei permessi oltre ciò che si può fare con .net
Pablo Claus

82

Una possibile semplificazione sarebbe la sottoclasse AuthorizeAttribute:

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

Uso:

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

Semanticamente è la stessa della risposta di Jim Schmehil.


3
Questo non ha funzionato per me, l'utente che ha effettuato l'accesso è stato in grado di ignorare l'attributo anche se l'utente non aveva alcun ruolo.
Urielzen

9
Questa risposta è migliore per quando usi le costanti come valori: es. [Ruoli (Costanti.Admin, Costanti.Proprietario]]
dalcam

3
questa è la risposta migliore
IgorShch

18

Per MVC4, usando un Enum( UserRoles) con i miei ruoli, uso un personalizzato AuthorizeAttribute.

Sulla mia azione controllata, faccio:

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

E io uso un'usanza del AuthorizeAttributegenere:

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

Questo fa parte del FNHMVC modificato di Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/


1
Il tuo metodo OnAuthorization richiederà all'utente di avere tutti i ruoli enumerati; era intenzionale o ti stai perdendo una pausa in quel ciclo?
Tieson T.

@Tieson: l'ho ispezionato abbastanza da vicino, sembra che in quel circuito sia necessaria una pausa.
OcelotXL

@TiesonT. e @ madrush, apprezzo la tua correzione, potrebbe davvero avere una pausa all'interno del loop. Cambierò il codice sopra.
Bernardo Loureiro,

L'enum UserRoles è carino. Lo dichiarate manualmente o è generato automaticamente in base al contenuto del DB?
Konrad Viltersten,

@KonradViltersten È manuale ma immagino che con Reflection e Dynamic class si possa fare l'autogenerazione
Bernardo Loureiro,

3

Un'altra soluzione chiara, è possibile utilizzare le costanti per mantenere la convenzione e aggiungere più attributi [Autorizza]. Controllalo:

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

Quindi nel controller:

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

14
AuthorizeAttributi multipli utilizzano la semantica AND e richiedono che TUTTE le condizioni siano soddisfatte (ovvero l'utente deve ricoprire ruoli sia di amministratore che di ospite).
trousyt

3

Se ti ritrovi ad applicare questi 2 ruoli spesso puoi avvolgerli nel loro Autorizzo. Questa è davvero un'estensione della risposta accettata.

using System.Web.Mvc;

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

E quindi applicare la nuova autorizzazione all'azione. Penso che questo appaia più pulito e legga facilmente.

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

1

Codice migliore con l'aggiunta di una sottoclasse 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 }
                  }
              );
            }
        }
    }

Come usarlo

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

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

1

Usando AspNetCore 2.x, devi andare in un modo un po 'diverso:

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

usalo così:

[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
Le risposte incluso il codice dovrebbero avere almeno una descrizione minima che spieghi come funziona il codice e perché risponde alla domanda. Inoltre, è necessario migliorare la formattazione della sezione di codice.
Roberto Caboni,

Eh? @Orsit Moel, sembra incollato nel thread sbagliato ...
Cameron Forward
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.