Speichern von Menüelementen mit Benutzerberechtigungen

11

Ich erstelle ein Menüsystem in PHP und MySQL. Ich werde verschiedene Menüs haben und mit jedem Menü wird eine Reihe von Menüpunkten verbunden sein.

Auf der Site habe ich auch unterschiedliche Benutzerberechtigungen, einige Benutzer können alle Menüelemente sehen und einige Elemente sind für einige Benutzer ausgeblendet. Ich bin gespannt, wie ich mit den Berechtigungen auf saubere Weise umgehen kann, damit in Zukunft problemlos mehr Benutzertypen hinzugefügt werden können.

Was ich bisher habe, ist ungefähr so:

-------------------
|Menus
-------------------
|id| |display name|
-------------------

----------------------------------------------------------
|Menu items
----------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| |permission|
----------------------------------------------------------

Ich denke, dass die permissionSpalte entweder eine durch Kommas getrennte Zeichenfolge sein kann, die ich mit der Berechtigungs-ID des aktuellen Benutzers vergleichen kann. Es könnte auch ein Verweis auf eine andere Tabelle sein, die alle möglichen Kombinationen der derzeit vorhandenen Berechtigungen definiert.

Eine Lösung könnte auch darin bestehen, einfach mehrere Menüelemente zu speichern, wobei der einzige Unterschied die Berechtigung ist, obwohl dies zu einer doppelten Speicherung und möglicherweise zu einer Verwaltung führen würde.

Ich würde gerne einige Gedanken darüber hören, wie man dies strukturiert und was als sauber, dynamisch und beschissen angesehen werden kann.

Vielen Dank.

Spanne
quelle
Haben Benutzer etwas gemeinsam? Normalerweise gruppieren Sie Menüelemente in Funktionsgruppen und weisen diesen Gruppen Benutzer zu (z. B. Administratorbenutzer, DB-Benutzer, Händler usw.). Dann verwalten Sie einfach die Gruppierungen, abhängig von der Wahl der Technologie - dies kann mithilfe von Active Directory verwaltet werden.
Michael
1
Sie erhalten hier viele gute Antworten, aber was Sie vielleicht lesen möchten, ist ACL. en.wikipedia.org/wiki/Access_control_list
Reactgular

Antworten:

17

Ich werde es anhand eines ER-Diagramms modellieren.

  • A PERMISSIONist der Zugriff, der einem ROLEauf einen bestimmten gewährt wird MENU_ITEM.
  • Eine ROLLE ist eine Reihe vordefinierter Berechtigungen, denen ein Name zugewiesen wird
  • A USERkann viele ROLLEN erhalten.
  • Durch die Zuweisung von Berechtigungen zu Rollen anstelle von Benutzern wird die Verwaltung von Berechtigungen erheblich vereinfacht.

Geben Sie hier die Bildbeschreibung ein

Dann können Sie eine Ansicht erstellen, damit Sie die Verknüpfungen nicht jedes Mal schreiben müssen:

create or replace view v_user_permissions
select
    distinct mi.id, mi.menu_id. mi.label. mi.link, mi.parent, mi.sort, u.user_id
from
    user u
    join user_role ur on (u.user_id = ur.user_id)
    join role ro on (ur.role_id = role.role_id)
    join permission per on (ro.role_id = per.role_id)
    join menu_item mi on (per.metu_item_id = mi.metu_item_id)

Jedes Mal, wenn Sie wissen möchten, auf welche Menüelemente ein Benutzer Zugriff hat, können Sie diese abfragen:

select * from v_user_permissions up where up.user_id = 12736 order by up.sort

BEARBEITEN:

Da einem Benutzer mehrere Rollen zugewiesen werden können, können sich die Berechtigungen der Rollen überschneiden, dh zwei unterschiedliche Rollen können auf denselben Menüpunkt zugreifen. Wenn Sie eine Rolle definieren, wissen Sie vorher nicht, ob sie einige Berechtigungen mit anderen Rollen gemeinsam hat. Da es sich jedoch um die Vereinigung von Mengen handelt, ist es nur wichtig, ob eine bestimmte Berechtigung Teil der Menge ist oder nicht, und nicht, wie oft sie erscheint, daher die distinctKlausel in der Ansicht.

Tulains Córdova
quelle
Super, danke. Ich kann nicht verstehen, warum ich das einfach nicht selbst erstellen konnte. Ich schätze, ich war blockiert und nicht erfahren :)
Spanne
Ack, ich dachte ich hätte verstanden, aber offensichtlich nicht. Wie kann ich mehrere Berechtigungen für einen einzelnen Menüpunkt haben?
Span
1
@span Da einem Benutzer mehrere Rollen gewährt werden können und sich die Berechtigungen der Rollen überschneiden können, können zwei unterschiedliche Rollen auf denselben Menüpunkt zugreifen. Wenn Sie eine Rolle definieren, wissen Sie vorher nicht, ob die Rolle zusammen mit anderen Rollen vergeben wird, die einige Berechtigungen gemeinsam haben. Da es bei diesem Problem jedoch um die Vereinigung von Mengen geht, ist es nur wichtig, ob eine bestimmte Berechtigung Teil der Menge ist oder nicht, und nicht, wie oft sie erscheint.
Tulains Córdova
Danke, ich werde deine Antwort so lange lesen, bis ich sie bekomme;). Ich glaube, mein Fehler bestand darin, zu glauben, dass eine einzige Berechtigung zusammen mit der Rolle zum Trennen der Menüelemente verwendet werden könnte. Es scheint, dass ich für jeden 'Typ' eines Menüpunkts eine Berechtigung benötige. Nochmals vielen Dank für Ihre Hilfe! Ich werde ein paar Venn-Diagramme zeichnen und sehen, ob ich es richtig
span
5

Wenn Sie eine durch Kommas getrennte Liste haben, müssen Sie jedes Mal einen Teilstring-Vergleich durchführen, wenn Sie eine Abfrage für das Menü durchführen. Das ist weniger als ideal.

Sie müssen die Tabelle normalisieren:

---------------------------------------------
|Menu items
---------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort|
---------------------------------------------

+---------------------------+
| menu_permission           |
|---------------------------|
| |menu_permission_id| (pk) |
| |menu_id| (fk)            |
| |permission|              |
+---------------------------+

Wenn Sie immer noch die durch Kommata getrennte Liste (aus irgendeinem Grund) möchten, können Sie es mit Dingen wie herausziehen group_concatin MySQL wm_concat in Oracle oder ähnliche Funktionen in anderen Sprachen.

Der Vorteil hierfür ist vielfältig.

Erstens gibt es die Praktikabilität des Anrufs. Wenn Sie einen Teilstring für eine beliebig große Zeichenfolge ausführen (wenn Sie die Größe festlegen, können Sie später Probleme beim Auffüllen der Zeichenfolge haben, sodass Sie Berechtigungen erhalten, wie astatt another_permission), müssen Sie die Zeichenfolge in jeder Zeile scannen. Dafür sind Datenbanken nicht optimiert.

Zweitens wird die Abfrage, die Sie schreiben, viel einfacher. Um festzustellen, ob die Berechtigung 'foo' in einer durch Kommas getrennten Liste vorhanden ist, müssen Sie nach 'foo' suchen.

... permission like "%foo%" ...

Dies ergibt jedoch ein falsches Positiv, wenn Sie auch die Erlaubnis 'foobar' haben. Jetzt müssen Sie also einen Test wie haben

... permission like "%,foo,%" ...

Dies gibt jedoch ein falsches Negativ aus, wenn sich 'foo' am Anfang oder Ende der Zeichenfolge befindet. Das führt zu so etwas

... (permission like "foo,%" 
  or permission like "%,foo,%" 
  or permission like "%,foo" ) ...

Sie werden feststellen, dass es sehr wahrscheinlich ist, dass Sie mehrere Scans der Zeichenfolge durchführen müssen. Dieser Weg führt zum Wahnsinn.

Beachten Sie, dass Ihnen bei all diesen die praktische Fähigkeit zur Parameterbindung fehlt (immer noch möglich, wird nur noch hässlicher).

Durch die Normalisierung des Feldes erhalten Sie eine viel größere Flexibilität und Lesbarkeit in Ihrer Datenbank. Du wirst es nicht bereuen.


quelle
Vielen Dank für Ihre großartige Antwort, sie hat mir mehr Wissen gegeben und ich bin sehr dankbar dafür, obwohl ich denke, dass die user61852-Lösung für den Moment am besten geeignet ist.
Span
3

Dieser klassische Ansatz dazu ist User -> UserGroupund wird dann mit a verbunden Menu -> MenuItem -> UserGroup. Verwenden eines ganzzahligen Werts zum Abwägen der Berechtigungsstufe.

-------------------
|Menu
-------------------
|id| |display name|
-------------------

-------------------------------------------------------------
|Menu Item
-------------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| | min_option1
-------------------------------------------------------------

-------------------------------------------
| User
-------------------------------------------
|id| | username | password | user_group_id
-------------------------------------------

-------------------------------------------
| User Group
-------------------------------------------
|id| option1 | option2 | option2
-------------------------------------------

Wenn Sie ein Menü für den aktuellen Benutzer anzeigen müssen. Sie können die Datenbank folgendermaßen abfragen.

SELECT * FROM `MenuItem`
    LEFT JOIN `UserGroup` ON (`MenuItem`.`user_group_id` = `UserGroup`.`id`)
    LEFT JOIN `User` ON (`UserGroup`.`id` = `User`.`user_group_id` AND `User`.`id` = $current_user_id)
        WHERE `MenuItem`.`menu_id` = $menu_id AND `UserGroup`.`option1` >= `MenuItem`.`min_option1`;

Dadurch werden nur Menüs ausgewählt, die für den aktuellen Benutzer basierend auf der Bedingung für sichtbar sind option1.

Wenn Sie alternativ die Gruppendetails des aktuellen Benutzers in der aktuellen Sitzung speichern, ist kein Join erforderlich.

SELECT * FROM `MenuItem` WHERE `MenuItem`.`menu_id` = $menu_id AND `MenuItem`.`min_option1` >= $user_group_option1;

Wenn Sie darüber sprechen, mehrere Berechtigungen pro Menüelement speichern zu wollen. Ich würde darauf achten, Benutzerrollen und Geschäftslogik nicht zu verwechseln.

Reactgular
quelle
2
Dieses Design würde es nur ermöglichen, jedes Menüelement an eine einzelne Benutzergruppe zu binden, was entweder begrenzte Menüs oder Datenvervielfältigung bedeutet. In Wirklichkeit möchten Sie im Idealfall eine Verknüpfungstabelle. Auch Ihre Wahl der DB-Tabelle, die als Plural bezeichnet wird, macht mich traurig;)
Ed James
@ EdWoodcock oh sehr guter Punkt. Ich hätte eine Berechtigungsstufe (int) wählen und diese dann mit der Gruppenebene des Benutzers vergleichen sollen. Ich werde das ändern. Beachten Sie, dass Pluralnamen Gewohnheit sind, die durch meine Verwendung von CakePHP verursacht werden. Was seltsam ist, da das Framework singuläre Aliase für Tabellen in Abfragen verwendet.
Reactgular
@MatthewFoscarini Keine Sorge, ich bin nicht wirklich gestört, solange eine Codebasis konsistent ist;)
Ed James
1
Tolle Antwort. Ich werde das beim nächsten Mal im Hinterkopf behalten, wenn ich etwas tue. Im Moment denke ich, dass die user61852-Lösung am besten geeignet ist, da nicht so viele Änderungen am vorhandenen Code erforderlich sind. Vielen Dank!
Span