Autorizza attributo con più ruoli


97

Vorrei aggiungere l'autorizzazione a un controller, per più ruoli contemporaneamente.

Normalmente sarebbe simile a questo:

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

Ma ho memorizzato i miei ruoli in consts, poiché potrebbero cambiare o essere estesi a un certo punto.

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

Non posso farlo, poiché la stringa deve essere nota in fase di compilazione:

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

C'è un modo per aggirare il problema?

Potrei scrivere una const che contiene semplicemente "RoleA, RoleB, RoleC" - ma non mi piacciono le stringhe magiche e questa è una stringa magica. Cambiare il nome di un ruolo e dimenticare di cambiare la stringa combinata sarebbe un disastro.

Sto usando MVC5. L'identità e il ruolo di ASP.NET sono noti in fase di compilazione.


stai usando public const string RoleA = "RoleA"; o come hai scritto in questione?
Mukesh Modhvadiya

Risposte:


188

Prova a creare un attributo di autorizzazione personalizzato come questo .

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

Supponendo che i tuoi ruoli saranno gli stessi per più controller, crea una classe helper:

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

Quindi usalo in questo modo:

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

12
Questa è un'idea degna di Mac Gyver;)
Christian Sauer

2
Molto bella soluzione :)
AUP

1
Mi piace molto anche questa soluzione, soprattutto perché posso lasciare che il mio ruolo sia un'enumerazione piuttosto che una stringa. Quali sarebbero un buon spazio dei nomi e posizione nella gerarchia del progetto per inserire questo attributo di autorizzazione personalizzato?
Simon Shine

4
Non sono sicuro di cosa stia succedendo qui, ma questo NON mi ha aiutato, qualsiasi utente indipendentemente dal ruolo è stato in grado di accedere al metodo.
Urielzen

2
Stesso problema di @Urielzen, ma è stato risolto dalla risposta di Jerry Finegan di seguito (utilizzando "System.Web.Mvc.AuthorizeAttribute e NOT System.Web.Http.AuthorizeAttribute")
RJB

13

Assicurati di derivare la tua classe di attributi personalizzati off System.Web.Mvc.AuthorizeAttributee NOT System.Web.Http.AuthorizeAttribute.

Mi sono imbattuto nello stesso problema. Una volta cambiato, tutto ha funzionato.

Puoi anche aggiungere quanto segue alla tua classe di attributi personalizzati:

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

Ho appena provato e ho trovato un riferimento alla libreria System.Web.Http.AuthorizeAttributeINVECE DISystem.Web.Mvc.AuthorizeAttribute
fraser jordan

10

Il modo migliore e più semplice che ho trovato per risolvere questo problema è semplicemente concatenare i ruoli nell'attributo Autorizza.

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

con CustomRole una classe con stringhe costanti come questa:

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

2
Prezioso; ma questo dovrebbe essere un commento; non una risposta.
GhostCat

1
Soluzione semplice ed elegante!
Iosif Bancioiu

Sia la tua risposta che la risposta accettata attiveranno l'autorizzazione se implementate correttamente (sto usando l'accettato in un'app Web di produzione). Proporre una modifica per rimuovere i commenti sulla risposta accettata.
Eric Eskildsen

3

Quello che ho fatto è la risposta in @Tieson

Modifico un po 'la sua risposta. Invece di string.Join perché non convertirlo in elenco?

Ecco la mia risposta:

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

Quindi controlla se il ruolo è valido sovrascrivendo 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);
            }
        }

Ed ecco fatto, ora sta convalidando se il ruolo è autorizzato ad accedere alla risorsa


1

Mi sembra che un attributo di autorizzazione personalizzato sia eccessivo per questo problema a meno che tu non abbia una grande quantità di ruoli.

Poiché la stringa deve essere nota in fase di compilazione, perché non creare una classe Role statica che contenga stringhe pubbliche dei ruoli definiti e quindi aggiungere stringhe separate da virgole con determinati ruoli che si desidera autorizzare:

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

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

E poi puoi usare l'attributo di autorizzazione in questo modo sulla classe controller o sul metodo controller (o entrambi):

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

1
Questo esempio non funziona, o almeno non nel modo in cui potresti pensare. Ad esempio, mentre romanzo il ADMIN_OR_VIEWERruolo nell'azione è ridondante perché non ti sarà permesso di accedere al Createmetodo se non hai già il ADMINruolo. In questo caso VIEWERnon sarà mai possibile invocare il Createmetodo.
John Leidegren

Anche questa soluzione non è scalabile. Ci sarà un punto in cui avrai troppi ruoli con azioni diverse e non dovresti creare tutte le combinazioni
EduLopez
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.