Rollenbasierte REST-API?

27

Ich erstelle eine REST-API, für die mehrere Benutzer mit unterschiedlichen Rollen Zugriff auf die darin enthaltenen Ressourcen haben.

Um den Umfang einfach zu halten, nehmen wir die Domain "Schüler / Lehrer / Klasse":

GET /students ist die Ressource, auf die zugegriffen werden soll.

Benutzer haben möglicherweise Rollen wie Schüler und / oder Lehrer

Die Schüler haben nur Zugang zu den Schülern ihrer Klassen. Die Lehrer haben Zugang zu den Schülern der Klassen, die sie unterrichten. Einige Anwendungen können ein Schüler sein UND auch andere Klassen unterrichten. Sie müssen Zugang zu den Schülern ihrer Klassen UND den Schülern der Klassen haben, die sie unterrichten.

Idealerweise möchte ich dies als zwei Funktionen implementieren - eine pro Rolle und dann "Vereinigung", wenn ein Benutzer mehrere Rollen hat.

Meine Frage ist: Welches Muster soll ich verwenden, um dies zu implementieren?

Äußerlich

  • Sollte ich meine API nach Rollen aufteilen? GET /teacher/studentsund GET /student/studentses scheint mir nicht richtig zu sein.
  • Behalte alles, ich bin eine Ressource (bevorzugt)

Im Inneren

Wie soll es intern implementiert werden?

  • Sollte jede Methode mit einem BIG-Schalter beginnen / wenn pro Rolle?
  • Sollte ich ein Repository pro Rolle implementieren?
  • Gibt es ein Designmuster, das mir dabei hilft?

Nebenbei bemerkt : Ich verwende ASP.NET-Web-API und Entity Framework 6 , aber die konzeptionelle Implementierung spielt keine Rolle.

Casper Jensen
quelle
3
"das ist eine großartige frage, ich würde gerne wissen, ob sie eine lösung dafür gefunden haben, weil ich versuche, etwas ähnliches zu tun. was ich denke, sollte es sein: Zuerst werden wir eine API implementieren, die alle benötigten Daten zurückgibt , dann verbindet sich jeder Client nicht direkt mit der API, sondern mit einem Proxy, der dafür verantwortlich ist, die Daten nach den Rollen dieses Benutzers zu filtern. "
Cleiton,

Antworten:

11

Sie sollten die API anhand von Ressourcen und nicht anhand von Rollen erstellen, z. B .:

/rest/students

sollte für jeden zugänglich sein, der eine Rolle hat, die es ihm ermöglicht, Schüler zu sehen.

Intern implementieren Sie rollenbasierte Sicherheit. Wie Sie vorgehen, hängt von den Details Ihrer Anwendung ab. Nehmen wir jedoch an, Sie haben eine Rollentabelle, jede Person hat eine oder mehrere Rollen, und diese Rollen bestimmen, auf was jede Person zugreifen kann. Sie haben bereits die Regeln für den Zugang zu Studenten festgelegt:

  • Die Schüler können in den von ihnen belegten Klassen auf die Schüler zugreifen
  • Lehrer können in den von ihnen unterrichteten Klassen auf die Schüler zugreifen

Also, wenn eine Person anruft:

/rest/students

Sie rufen eine Methode auf, die auf Schüler zugreift und die Rolle der Person übergibt. Hier ist ein Pseudocode:

roles = person.roles; //array
students = getStudents( roles );
return students;

Auf diese Weise können Sie die Schüler für jede Rolle mit separaten Aufrufen abrufen, z.

factory = getFactory();
classes= [];
students = [];
for( role in roles ){
    service = factory.getService( role );
    // implementation details of how you get classes for student/teacher are hidden in the service
    classes = classes.merge( service.getClasses( person ) );
    // classes[] has class.students[]
    // loop on classes and add each student to students, or send back classes with nested students? depends on use case
  }
}

Das ist eine sehr grobe Idee für das, was Sie tun könnten und wird nicht unbedingt Ihren spezifischen Bedürfnissen entsprechen, aber es sollte Ihnen ein Gefühl für die beteiligten Teile geben. Wenn Sie die Klassen mit jedem aufgelisteten Schüler zurückgeben möchten, ist dies ein guter Ansatz. Wenn Sie nur die Schüler möchten, können Sie sie aus jeder Klasse extrahieren und sie zu einer Sammlung von Schülern zusammenführen.

Nein, Sie sollten kein separates Repository pro Rolle haben. Alles, was die Rolle tut, ist zu bestimmen, wie Sie die Daten erhalten und was Sie möglicherweise mit den Daten tun können (z. B. können Lehrer die Noten der Schüler eingeben). Die Daten selbst sind die gleichen.

Bei diesem Ansatz wird Factory Pattern verwendet, um den Service zu abstrahieren, der Daten basierend auf der Rolle abruft. Es kann angebracht oder nicht angebracht sein, getrennte Dienste nach Rollen zu haben. Ich mag diesen Ansatz, weil er die Codemenge in jeder Phase des Programms minimiert und lesbarer macht als ein Schalter oder ein if-Block.

Robert Munn
quelle
1
Danke für die Antwort. Am Ende habe ich etwas gemacht, wie Sie es vorgeschlagen haben. Mit LINQ2SQL (C #) konnte ich die Abfrage an jede "Rolle" übergeben und für jede Rolle, die der Benutzer erhielt, ein "Where" zuweisen. Das Ergebnis wäre eine SQL-Anweisung mit einer "OR" -Bedingung für jede Rolle, auf die der Benutzer Zugriff hat. Wenn einem Benutzer keine Rollen zugewiesen sind, gebe ich einfach Enumarable.Empty () als Anrufer zurück.
Casper Jensen
0

Finden Sie einen Stift und ein Papier und beginnen Sie mit der Modellierung Ihres Systems.

Sie werden feststellen, dass Sie wahrscheinlich eine Domänenentität mit dem Namen PERSON benötigen. Da sowohl STUDENTEN als auch LEHRER eine PERSON sind, können Sie eine abstrakte Entität namens PERSON mit generischen Attributen wie Vorname, Nachname usw. erstellen. Ein LEHRER -> ist eine -> Person. Jetzt können Sie versuchen, Merkmale für einen LEHRER zu finden, die für STUDENTEN nicht zutreffen. zB Ein LEHRER unterrichtet KLASSEN zu einem oder mehreren Fächern.

Das Erzwingen der Sicherheit wird als nicht funktionaler Aspekt Ihrer Anwendung angesehen. Es ist ein Querschnittsthema , das außerhalb Ihrer "Geschäftslogik" behandelt werden sollte. Wie @Robert Munn betont, sollten die ROLLEN alle an einem Ort aufbewahrt werden. Die Verwendung von Rollen zur Einschränkung des Zugriffs auf bestimmte Funktionen ist eher grobkörnig, und das Konzept wird als rollenbasierte Zugriffssteuerung (RBAC) bezeichnet.

Um zu überprüfen, ob ein Lehrer die Noten eines Schülers sehen darf oder nicht, muss dies in Ihrem Domain-Modell angegeben werden. Angenommen, ein Lehrer hat eine Klasse zum Thema Programmierung. Sie würden wahrscheinlich in Ihrem Modell ausdrücken, dass Schüler Klassen für verschiedene Fächer besuchen. Hier setzt die Anwendungs- / Geschäftslogik an. Dies ist eine Logik, die Sie mithilfe einer testgetriebenen Entwicklung überprüfen können.

Sie sollten Ihre Ressourcen aufteilen, um Ihre Anwendung testbar und modular zu machen.

Wie auch immer, der beste Weg, um wirklich zu zeigen, was ich meine, ist, es mit Code zu zeigen :) Hier ist eine GitHub-Seite: https://github.com/thomasandersen77/role-based-rest-api

Viel Glück :)

Thomas Andersen
quelle
3
Ihr Link ist weg ...
Cleiton