Attribut mit mehreren Rollen autorisieren

97

Ich möchte einem Controller eine Autorisierung für mehrere Rollen gleichzeitig hinzufügen.

Normalerweise würde das so aussehen:

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

Aber ich habe meine Rollen in consts gespeichert, da sie sich irgendwann ändern oder erweitern könnten.

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

Ich kann dies nicht tun, da die Zeichenfolge zur Kompilierungszeit bekannt sein muss:

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

Gibt es eine Möglichkeit, das Problem zu umgehen?

Ich könnte eine Konstante schreiben, die einfach "RoleA, RoleB, RoleC" enthält - aber ich mag keine magischen Strings und dies ist ein magischer String. Das Ändern des Namens einer Rolle und das Vergessen, die kombinierte Zeichenfolge zu ändern, wäre eine Katastrophe.

Ich benutze MVC5. Die ASP.NET-Identität und die Rolle sind zur Kompilierungszeit bekannt.

Christian Sauer
quelle
Verwenden Sie den öffentlichen const string RoleA = "RoleA"; oder wie du in frage geschrieben hast?
Mukesh Modhvadiya

Antworten:

188

Versuchen Sie, ein benutzerdefiniertes Berechtigungsattribut wie dieses zu erstellen .

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

Angenommen, Ihre Rollen sind für mehrere Controller gleich, erstellen Sie eine Hilfsklasse:

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

Dann benutze es so:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}
MacGyver
quelle
12
Das ist eine Idee, die Mac Gyver würdig ist;)
Christian Sauer
2
Sehr schöne Lösung :)
bis
1
Ich mag diese Lösung auch sehr, besonders weil ich meine Rolle eher eine Aufzählung als eine Zeichenfolge sein lassen kann. Was wäre ein guter Namespace und Speicherort in der Projekthierarchie, um dieses benutzerdefinierte Berechtigungsattribut zu platzieren?
Simon Shine
4
Ich bin nicht sicher, was hier vor sich geht, aber das hat mir NICHT geholfen, jeder Benutzer unabhängig von der Rolle konnte auf die Methode zugreifen.
Urielzen
2
Gleiches Problem wie @Urielzen, aber es wurde durch die Antwort von Jerry Finegan (unter Verwendung von "System.Web.Mvc.AuthorizeAttribute und NICHT System.Web.Http.AuthorizeAttribute") behoben
RJB
13

Stellen Sie sicher, dass Sie Ihre benutzerdefinierte Attributklasse deaktivieren System.Web.Mvc.AuthorizeAttributeund NICHT System.Web.Http.AuthorizeAttribute.

Ich bin auf das gleiche Problem gestoßen. Sobald ich es geändert habe, hat alles funktioniert.

Möglicherweise möchten Sie Ihrer benutzerdefinierten Attributklasse auch Folgendes hinzufügen:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
Jerry Finegan
quelle
Ich habe gerade versucht , diese und fand die Bibliothek Referenzierung System.Web.Http.AuthorizeAttributeSTATTSystem.Web.Mvc.AuthorizeAttribute
fraser jordan
10

Der beste und einfachste Weg, um dieses Problem zu lösen, besteht darin, Rollen im Attribut "Autorisieren" zu verketten.

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

mit CustomRole eine Klasse mit konstanten Zeichenfolgen wie folgt:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}
ChristopheHvd
quelle
2
Wertvoll; aber das sollte ein Kommentar sein; keine Antwort.
GhostCat
1
Einfache und elegante Lösung!
Iosif Bancioiu
Sowohl Ihre Antwort als auch die akzeptierte Antwort lösen bei korrekter Implementierung eine Autorisierung aus (ich verwende die in einer Produktions-Web-App akzeptierte). Vorschlag einer Bearbeitung, um die Kommentare zur akzeptierten Antwort zu entfernen.
Eric Eskildsen
3

Was ich getan habe, ist die Antwort in @Tieson

Ich ändere seine Antwort ein wenig. Anstelle von string.Join warum nicht in Liste konvertieren?

Hier ist meine Antwort:

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

Überprüfen Sie anschließend, ob die Rolle gültig ist, und überschreiben Sie 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);
            }
        }

Und da Sie es haben, wird jetzt überprüft, ob die Rolle zum Zugriff auf die Ressource berechtigt ist

Christopher Enriquez
quelle
1

Ich bin der Meinung, dass ein benutzerdefiniertes Berechtigungsattribut für dieses Problem übertrieben ist, es sei denn, Sie haben eine große Anzahl von Rollen.

Da die Zeichenfolge zur Kompilierungszeit bekannt sein muss, erstellen Sie eine statische Rollenklasse, die öffentliche Zeichenfolgen der von Ihnen definierten Rollen enthält, und fügen Sie dann durch Kommas getrennte Zeichenfolgen mit bestimmten Rollen hinzu, die Sie autorisieren möchten:

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

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

Und dann können Sie das Authorize-Attribut wie folgt für die Controller-Klasse oder die Controller-Methode (oder beides) verwenden:

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}
Robert Tuttle
quelle
1
Dieses Beispiel funktioniert nicht oder zumindest nicht so, wie Sie vielleicht denken. Während neu ist die ADMIN_OR_VIEWERRolle in der Aktion beispielsweise redundant, da Sie nicht zur CreateMethode gelangen dürfen, wenn Sie die ADMINRolle noch nicht haben . In diesem Fall VIEWERkann die CreateMethode niemals aufgerufen werden.
John Leidegren
Diese Lösung ist auch nicht skalierbar. Es wird einen Punkt geben, an dem Sie zu viele Rollen mit unterschiedlichen Aktionen haben und nicht jede Kombination erstellen sollten
EduLopez