Memorizzazione di voci di menu con autorizzazioni utente


11

Sto creando un sistema di menu in PHP e MySQL. Avrò diversi menu diversi e ogni menu avrà una serie di voci di menu collegate ad esso.

Sul sito, ho anche diverse autorizzazioni utente, alcuni utenti possono vedere tutte le voci di menu e alcuni elementi sono nascosti da alcuni utenti. Sono curioso di sapere come potrei gestire le autorizzazioni in modo pulito che consentirà di aggiungere facilmente altri tipi di utenti in futuro.

Quello che ho finora è qualcosa del genere:

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

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

Sto pensando che la permissioncolonna potrebbe essere una stringa separata da virgola che posso confrontare con l'id di autorizzazione dell'utente corrente. Potrebbe anche essere un riferimento ad alcune altre tabelle che definiscono tutte le possibili combinazioni delle autorizzazioni attualmente esistenti.

Una soluzione potrebbe anche essere quella di archiviare semplicemente più voci di menu in cui l'unica differenza è l'autorizzazione, sebbene ciò comporterebbe una duplicazione di archiviazione e forse un problema da amministrare.

Mi piacerebbe sentire qualche pensiero su come strutturare questo e cosa potrebbe essere considerato pulito, dinamico e scadente.

Grazie.


Gli utenti hanno qualcosa in comune? In genere si raggruppano le voci di menu in gruppi funzionali e si assegnano gli utenti a tali gruppi (ad esempio: utenti amministratori, utenti DB, operatori commerciali, ecc.). Quindi gestisci solo i raggruppamenti, a seconda della scelta della tecnologia: questo può essere gestito usando qualcosa come Active Directory.
Michael,

1
Stai ricevendo molte buone risposte qui, ma quello che potresti voler leggere è ACL. en.wikipedia.org/wiki/Access_control_list
Reactgular

Risposte:


17

Lo modellerò usando un diagramma ER.

  • A PERMISSIONè l'accesso concesso a a ROLEsu un dato MENU_ITEM.
  • Un RUOLO è un insieme di autorizzazioni predefinite a cui viene assegnato un nome
  • A USERpuò avere molti ruoli assegnati ad esso.
  • Con le autorizzazioni assegnate ai ruoli anziché agli utenti, l'amministrazione delle autorizzazioni è molto più semplice.

inserisci qui la descrizione dell'immagine

Quindi puoi creare una vista in modo da non dover scrivere i join ogni volta:

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)

Quindi ogni volta che vuoi sapere a quali voci di menu ha accesso un utente, puoi interrogarlo:

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

MODIFICARE:

Poiché un utente può disporre di più ruoli, le autorizzazioni dei ruoli possono sovrapporsi, ovvero due ruoli distinti possono avere accesso alla stessa voce di menu. Quando definisci un ruolo, non sai in anticipo se avrà alcune autorizzazioni in comune con altri ruoli. Ma poiché si tratta dell'unione di insiemi, importa solo se una determinata autorizzazione fa parte o meno dell'insieme, non quante volte appare, quindi la distinctclausola nella vista.


Fantastico, grazie. Non riesco a capire perché non riesco a elaborarlo da solo. Immagino che era bloccata e non con esperienza :)
durata

Ack, pensavo di aver capito ma ovviamente no. Come posso avere più autorizzazioni per una singola voce di menu?
durata

1
@span Poiché un utente può avere più ruoli concessi e le autorizzazioni dei ruoli possono sovrapporsi, vale a dire due ruoli distinti possono avere accesso alla stessa voce di menu. Quando si definisce un ruolo che non si conosce in anticipo se il ruolo verrà concesso insieme ad altri che hanno alcune autorizzazioni in comune con esso. Ma poiché questo problema riguarda l'unione di insiemi, importa solo se una determinata autorizzazione fa parte o meno dell'insieme, non quante volte appare.
Tulains Córdova,

Grazie, continuerò a leggere la tua risposta fino a quando non la avrò;). Penso che il mio errore sia stato nel pensare che una singola autorizzazione potesse essere utilizzata insieme al ruolo per separare le voci di menu. Sembra che mi serva un'autorizzazione per ogni "tipo" di voce di menu. Ancora una volta, grazie per il tuo aiuto! Disegnerò alcuni diagrammi di Venn e vedrò se riesco a girarci attorno la testa correttamente \ o /
span

5

Avere un elenco separato da virgole significa fare un confronto di sottostringhe ogni volta che si esegue una query sul menu. Questo è meno che ideale.

Devi normalizzare la tabella:

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

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

Se desideri ancora l'elenco separato da virgole (per qualche motivo), puoi estrarlo con cose come group_concatin mysql wm_concat in Oracle o funzioni simili in altre lingue.

Il vantaggio è multiplo.

Innanzitutto, c'è la praticità della chiamata. Fare una sottostringa su una stringa arbitrariamente grande (se si corregge la dimensione, si possono avere problemi in seguito con il riempimento della stringa in modo da iniziare a ottenere autorizzazioni come ainvece di another_permission) significa scansionare la stringa su ogni riga. Questo non è qualcosa per cui i database sono ottimizzati.

In secondo luogo, la query che scrivi diventa molto più semplice. Per determinare se l'autorizzazione "pippo" esiste in un elenco separato da virgole, è necessario controllare "pippo".

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

Tuttavia, questo darà un falso positivo se si dispone anche dell'autorizzazione "foobar". Quindi ora devi fare un test simile

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

ma questo darà un falso negativo se 'pippo' è all'inizio o alla fine della stringa. Questo porta a qualcosa di simile

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

Noterai che è molto probabile che dovrai eseguire più scansioni della stringa. In questo modo porta alla follia.

Nota con tutto ciò che ti manca la capacità pratica di eseguire il binding dei parametri (ancora possibile, diventa ancora più brutto).

La normalizzazione del campo offre una maggiore flessibilità e leggibilità nel database. Non te ne pentirai.


Grazie per la tua grande risposta, mi ha dato più conoscenza e ne sono grato anche se penso che la soluzione user61852 si adatterà meglio per ora.
durata

3

Questo approccio classico a questo è User -> UserGroupe quindi associato a Menu -> MenuItem -> UserGroup. Utilizzo di un valore intero per pesare il livello di autorizzazione.

-------------------
|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
-------------------------------------------

Quando è necessario visualizzare un menu per l'utente corrente. È possibile eseguire una query sul database in questo modo.

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`;

Ciò selezionerà solo i menu che sono visibili all'utente corrente in base alla condizione per option1.

In alternativa, se si memorizzano i dettagli del gruppo dell'utente corrente nella sessione corrente, non è richiesto alcun join.

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

Quando si parla di voler memorizzare più autorizzazioni per voce di menu. Starei attento a non confondere i ruoli utente e la logica aziendale.


2
Questo design consentirebbe a ciascun MenuItem di essere legato a un singolo Gruppo utenti, ovvero menu limitati o duplicazione dei dati. In realtà, si desidera una tabella di collegamenti, idealmente. Inoltre, la tua scelta di denominare le tabelle DB come plurali mi rende triste;)
Ed James,

@EdWoodcock oh ottimo punto. Avrei dovuto scegliere un livello di autorizzazione (int) e confrontarlo con il livello di gruppo dell'utente. Lo cambierò. Nota, l'abitudine di nomi plurali causati dal mio uso di CakePHP. Il che è strano, perché il framework utilizza alias singolari per le tabelle nelle query.
Reactgular,

@MatthewFoscarini Non preoccuparti, non mi preoccupo molto finché una base di codice è coerente;)
Ed James,

1
Risposta fantastica. Lo terrò a mente la prossima volta che farò qualcosa di simile. Per ora penso che la soluzione user61852 si adatterà meglio poiché non richiede tante modifiche al codice esistente. Grazie!
durata
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.