it-swarm.com.de

Rollenbasierte REST API?

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 Domäne "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 lautet: Welches Muster soll ich zur Implementierung verwenden?

Extern

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

Intern

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 Entwurfsmuster, das mir dabei hilft, dies zu erreichen?

Als Nebenkommentar: Ich verwende ASP.NET Web API und Entity Framework 6 , aber es ist wirklich nicht wichtig für die konzeptionelle Implementierung.

27
Casper Jensen

Sie sollten die API auf Ressourcen und nicht auf Rollen ausrichten, z.

/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 Zugriff auf Schüler festgelegt:

  • schüler können auf Schüler in den Klassen zugreifen, die sie belegen
  • lehrer können auf Schüler in den Klassen zugreifen, die sie unterrichten

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;

und bei dieser Methode könnten Sie die Schüler für jede Rolle mit separaten Aufrufen erhalten, 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 Vorstellung davon, was Sie tun könnten und wird nicht unbedingt Ihren spezifischen Bedürfnissen entsprechen, aber es sollte Ihnen einen Eindruck von den beteiligten Teilen 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 haben möchten, können Sie sie aus jeder Klasse extrahieren und zu einer Sammlung von Schülern zusammenführen.

Nein, Sie sollten kein separates Repository pro Rolle haben. Die Rolle bestimmt lediglich, 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 Mustern wird bei diesem Ansatz Factory Pattern verwendet, um den Dienst zu abstrahieren, der Daten basierend auf der Rolle erhält. Es kann angebracht sein oder nicht, 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.

12
Robert Munn

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

Sie werden feststellen, dass Sie wahrscheinlich eine Domain-Entität namens 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 nicht für STUDENTEN gelten. z.B. Ein LEHRER unterrichtet KLASSE (n) in Bezug auf ein oder mehrere Fächer.

Das Durchsetzen 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 ROLLE (n) alle an einem Ort aufbewahrt werden. Die Verwendung von Rollen zur Einschränkung des Zugriffs auf bestimmte Funktionen ist ziemlich grobkörnig, und das Konzept heißt rollenbasierte Zugriffssteuerung (RBAC).

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

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

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

Viel Glück :)

0
Thomas Andersen