Memorizzazione di voci di menu con autorizzazioni utente

10

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

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 gestire le autorizzazioni in modo pulito, in modo da poter 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|
----------------------------------------------------------

Penso che la colonna permission possa essere una stringa separata da virgole che posso confrontare con l'ID di autorizzazione dell'utente corrente. Potrebbe anche essere un riferimento ad un'altra tabella che definisce tutte le possibili combinazioni delle autorizzazioni attualmente esistenti.

Una soluzione potrebbe anche consistere semplicemente nel memorizzare più voci di menu dove l'unica differenza è il permesso, sebbene ciò porterebbe a una duplicazione dell'archiviazione e forse un dolore da amministrare.

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

Grazie.

    
posta span 29.07.2013 - 15:21
fonte

3 risposte

15

Lo modellerò usando un diagramma ER.

  • Un PERMISSION è l'accesso concesso a ROLE su un dato MENU_ITEM .
  • Un RUOLO è un insieme di autorizzazioni predefinite a cui viene assegnato un nome
  • A USER può avere molti ruoli a lui concessi.
  • Avere le autorizzazioni assegnate ai ruoli anziché agli utenti, semplifica notevolmente l'amministrazione delle autorizzazioni.

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

EDIT:

Poiché un utente può avere più ruoli concessi, 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é riguarda l'unione di insiemi, importa solo se un determinato permesso fa parte o meno del set, non quante volte appare, quindi la clausola distinct nella vista.

    
risposta data 29.07.2013 - 15:49
fonte
5

Avere una lista separata da virgola significa fare un confronto di sottostringa ogni volta che si esegue una query sul menu. Questo non è l'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 vuoi ancora la lista separata da virgola (per qualche motivo), puoi estrarla con cose come group_concat in mysql wm_concat in oracle o funzioni simili in altre lingue.

Il vantaggio per questo è multipla.

In primo luogo, c'è la praticità della chiamata. Eseguendo una sottostringa su una stringa arbitrariamente grande (se si risolve la dimensione, è possibile avere problemi in seguito con il riempimento della stringa in modo da iniziare a ottenere autorizzazioni come a anziché another_permission ) significa eseguire la scansione della 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 il permesso 'pippo' esiste in un elenco separato da virgole, devi controllare 'foo'.

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

Tuttavia, questo darà un falso positivo se hai anche il permesso 'foobar'. Quindi ora devi fare un test come

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

ma questo darà un falso negativo se 'pippo' è all'inizio o alla fine della stringa. Ciò 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. Questo modo porta alla pazzia.

Nota con tutti questi ti manca l'abilità pratica di eseguire il binding dei parametri (ancora possibile, diventa ancora più brutto).

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

    
risposta data 29.07.2013 - 15:49
fonte
3

Questo approccio classico a questo è User -> UserGroup e quindi associato a Menu -> MenuItem -> UserGroup . Utilizzo di un valore intero per valutare 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. Puoi interrogare il 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';

Questo 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 parli di voler memorizzare più permessi per voce di menu. Farei attenzione a non confondere i ruoli utente e la logica aziendale.

    
risposta data 29.07.2013 - 15:49
fonte

Leggi altre domande sui tag