Se stai creando un CMS, probabilmente avrai bisogno di diversi ruoli utente: superuser, amministratori, utenti, con diversi livelli di autorizzazione. Troppo complicato da codificare? Inserisci l'ACL di CakePHP (Access Control List). Con la corretta configurazione, controllerai le autorizzazioni degli utenti con una sola riga.
L'ACL consente di creare una gerarchia di utenti con i rispettivi ruoli. Ecco un rapido esempio.
In questo tutorial, configureremo un ACL per un blog semplice. Se non hai ancora provato come iniziare con CakePHP (e parte 2) qui su Nettuts +, per favore fallo e poi ritorna, dato che daremo per scontate le basi del framework.
Con questa gerarchia, possiamo assegnare diverse autorizzazioni per ogni ruolo:
Ogni autorizzazione sarà data al gruppo, non all'utente; quindi se l'utente # 6 viene promosso ad Admin, verrà controllato rispetto all'autorizzazione del gruppo - non suo. Questi ruoli e nodi figlio (utenti) sono chiamati oggetti Richieste di accesso o ARO.
Ora, dall'altra parte, abbiamo gli Access Control Objects o ACO. Questi sono gli oggetti da controllare. Sopra ho menzionato i messaggi e gli utenti. Normalmente, questi oggetti sono direttamente collegati ai modelli, quindi se abbiamo un modello Post, avremo bisogno di un ACO per questo modello.
Ogni ACO ha quattro permessi di base: creare, leggere, aggiornare ed eliminare. Puoi ricordarli con la parola chiave CRUD. C'è una quinta autorizzazione, l'asterisco, che è una scorciatoia per l'accesso completo.
Utilizzeremo solo due ACO per questo tutorial: Post e Utente, ma puoi crearne quanti ne hai bisogno.
Procediamo per creare le tabelle del database. Puoi trovare questo codice in db_acl.sql
all'interno della tua app config / sql
elenco.
CREATE TABLE acos (id INTEGER (10) UNSIGNED NOT NULL AUTO_INCREMENT, parent_id INTEGER (10) DEFAULT NULL, modello VARCHAR (255) DEFAULT ", foreign_key INTEGER (10) UNSIGNED DEFAULT NULL, alias VARCHAR (255) DEFAULT", lft INTEGER ( 10) DEFAULT NULL, rght INTEGER (10) DEFAULT NULL, PRIMARY KEY (id)); CREATE TABLE aros_acos (id INTEGER (10) UNSIGNED NOT NULL AUTO_INCREMENT, aro_id INTEGER (10) UNSIGNED NOT NULL, aco_id INTEGER (10) UNSIGNED NOT NULL, _create CHAR (2) NOT NULL DEFAULT 0, _read CHAR (2) NOT NULL DEFAULT 0, _update CHAR (2) NOT NULL DEFAULT 0, _delete CHAR (2) NOT NULL DEFAULT 0, PRIMARY KEY (id)); CREATE TABLE aros (id INTEGER (10) UNSIGNED NOT NULL AUTO_INCREMENT, parent_id INTEGER (10) DEFAULT NULL, modello VARCHAR (255) DEFAULT ", foreign_key INTEGER (10) UNSIGNED DEFAULT NULL, alias VARCHAR (255) DEFAULT", lft INTEGER ( 10) DEFAULT NULL, rght INTEGER (10) DEFAULT NULL, PRIMARY KEY (id));
Ora possiamo iniziare a creare nodi ARO e ACO, ma hey, non abbiamo utenti! Dovremo creare un sistema di autenticazione di base.
Poiché questo tutorial è destinato agli sviluppatori di CakePHP con una conoscenza da base a moderata del framework, fornirò il codice e una breve spiegazione. Tuttavia, il sistema di autenticazione non è l'obiettivo di questo tutorial.
La tabella MySQL:
CREATE TABLE users (id INTEGER (10) UNSIGNED AUTO_INCREMENT KEY, nome utente TEXT, password TEXT);
Il modello utente (modelli / user.php
)
Il controller degli utenti (controllori / users_controller.php
)
Auth-> userModel = 'Utente'; $ This-> Auth-> consentire ( '*'); function register () if (! empty ($ this-> data)) // Qui dovresti convalidare il nome utente (lunghezza minima, lunghezza massima, per non includere caratteri speciali, non esistenti già, ecc.) // Anche come password se ($ this-> User-> validates ()) $ this-> User-> save ($ this-> data); // Leggiamo i dati che abbiamo appena inserito $ data = $ this-> User-> read (); // Usalo per autenticare l'utente $ this-> Auth-> login ($ data); // Quindi reindirizza $ this-> redirect ('/'); login () if (! empty ($ this-> data)) // Se il nome utente / password corrispondono se ($ this-> Auth-> login ($ this-> data)) $ this -> redirect ( '/'); else $ this-> User-> invalidate ('username', 'La combinazione di nome utente e password non è corretta!'); function logout () $ this-> Auth-> logout (); $ This-> redirect ( '/'); ?>
Dal momento che abbiamo nuovi elementi, esaminiamoli. In primo luogo, stiamo impostando a $ componenti
variabile. Questa variabile include tutti i componenti dell'array. Avremo bisogno del auth componente, che è un componente principale, così come gli helper HTML e Form, ma poiché non è incluso di default da Cake, dovremo includerlo manualmente.
Il componente Auth gestisce alcuni meccanismi di autenticazione di base: ci aiuta ad accedere a un utente e gestisce per noi una sessione dell'utente autenticato, oltre a gestire la disconnessione e l'autorizzazione di base per gli ospiti. Inoltre, blocca automaticamente la password. Spiegherò come chiamare ciascuna funzione nei seguenti paragrafi.
Successivamente, stiamo creando una funzione chiamata beforeFilter
. Questa è una funzione di callback e ci consente di impostare alcune azioni prima che venga elaborata tutta la logica del controller. Il componente Auth ci richiede di specificare un modello da utilizzare, in questo caso l'utente. Quindi, per impostazione predefinita, negherà tutto l'accesso agli utenti che non hanno effettuato l'accesso. Dovremo sovrascrivere questo comportamento con permettere()
che richiede un parametro. Questo parametro può essere un asterisco, specificando che tutti i metodi all'interno di tale controller possono essere accessibili da utenti non autenticati. Oppure, può essere passato un array con le funzioni a cui possono accedere gli utenti non autenticati. In questo caso, poiché abbiamo solo tre funzioni, le seguenti linee sono la stessa cosa.
$ This-> Auth-> consentire ( '*'); $ this-> Auth-> allow (array ('register', 'login', 'logout'));
Per il accesso()
funzione, il componente Auth gestirà tutte le meccaniche di accesso per noi. Dovremo solo fornire la funzione con un array con due chiavi: il nome utente e la password. Queste chiavi possono essere modificate, ma per impostazione predefinita, entrambe nome utente
e parola d'ordine
i campi saranno confrontati con il database e restituiranno true se l'utente è stato autenticato.
Infine, la funzione di login del controller proverà ad abbinare una combinazione nome utente / password al database.
Si prega di notare che questo codice è molto semplice. Dovrai convalidare i caratteri del nome utente, se esiste il nome utente, la lunghezza minima per la password e così via.
La vista del registro (viste / utenti / register.ctp
)
Registra il tuo account
La vista di accesso (viste / utenti / login.ctp
)
Accedi al tuo account
Aperto / Utenti / registro
nel tuo browser web e registra un nuovo account. suggerisco Admin
come username e 123
come password e se la sessione scade basta andare / Utenti / login
e inserisci la combinazione nome utente / password che hai appena creato.
Non stiamo nemmeno lavorando con ACL, ma possiamo già negare la pubblicazione, la modifica e l'eliminazione di post. Apri il controller dei tuoi post e aggiungi il componente Auth.
var $ components = array ('Auth');
Ora vai a / messaggi
nel tuo browser web. Se hai effettuato l'accesso, dovresti vedere i post, ma se non lo sei, verrai reindirizzato a / Utenti / login
. Semplicemente includendo il componente Auth, tutte le azioni sono per default negate agli ospiti. Dobbiamo negare tre azioni per utenti non autorizzati: creare, modificare ed eliminare. In altri termini, dovremo consentire l'indice e la visualizzazione.
function beforeFilter () $ this-> Auth-> userModel = 'Utente'; $ this-> Auth-> allow (array ('index', 'view'));
Vai a modificare o creare un post; se non hai effettuato l'accesso, dovresti essere reindirizzato a / Utenti / login
. Tutto sembra funzionare abbastanza bene, ma per quanto riguarda i punti di vista? I link di modifica ed eliminazione vengono mostrati a tutti. Dovremmo fare un condizionale.
Ma prima di approfondire, vediamo come funziona la funzione user () di Auth. Copia e incolla queste righe nella funzione indice.
$ user = $ this-> Auth-> user (); pr ($ user);
Apri i tuoi / messaggi
nel tuo browser e, se loggato, il pr ()
getterò qualcosa come questo.
Array ([User] => Array ([id] => 1 [username] => admin))
Il utente()
la funzione restituisce un array proprio come farebbe un modello. Se avessimo più di tre campi (la password non è inclusa), saranno mostrati nell'array. Se non sei loggato, l'array sarà vuoto, quindi puoi sapere che un utente ha effettuato l'accesso se Auth utente()
l'array non è vuoto.
Ora, Auth è un componente, pensato per essere utilizzato in un controller. Dobbiamo sapere da una vista se un utente è loggato, preferibilmente tramite un helper. Come possiamo usare un componente all'interno di un aiutante? CakePHP è così fantastico e flessibile che è possibile.
class AccessHelper extends Helper var $helpers = array("Session"); function isLoggedin() App::import('Component', 'Auth'); $auth = new AuthComponent(); $auth->Sessione = $ this-> Session; $ utente = $ auth-> utente (); return! empty ($ user); ?>
Salva questo snippet in Visto / aiutanti
come access.php
. Ora vediamo il codice, riga per riga. In primo luogo, stiamo impostando a $ aiutanti
var. Gli helper possono includere altri aiutanti, proprio come $ componenti
può. Il componente Session è richiesto per il componente Auth, ma non abbiamo accesso a questo componente all'interno di un helper. Fortunatamente abbiamo un aiutante di sessione, che ci aiuterà.
Successivamente, creiamo una funzione e utilizziamo App :: import
che ci permetterà di importare un elemento che normalmente non avremmo accesso. La riga successiva crea il componente Auth in a $ auth
variabile, e ora un piccolo trucco sporco; poiché il componente Auth legge la sessione per sapere se siamo loggati o meno, richiede il componente Session, ma dal momento che lo stiamo importando da un posto a cui non dovrebbe appartenere, dovremo dargli un nuovo oggetto Session. Finalmente, stiamo usando utente()
e impostandolo su $ user
e restituisce true se la variabile non è vuota, altrimenti false.
Torniamo al controller dei post e procediamo ad aggiungere l'helper.
var $ helpers = array ('Access');
L'helper di accesso è ora accessibile dalla vista. Aperto index.ctp
nel Visto / messaggi
e sostituire questa linea.
"> modifica | echo $html->link ('cancella', '/posts/delete/'.$post['Post']['id'], NULL, 'Sei sicuro?'); ?>
Con questo.
if($access->isLoggedin ()):?>"> modifica | echo $html->link ('cancella', '/posts/delete/'.$post['Post']['id'], NULL, 'Sei sicuro?'); ?> endif; ?>
Torna al tuo browser web, ricarica la pagina dell'indice e se hai effettuato l'accesso, vedrai la modifica e l'eliminazione dei link per ogni post. Altrimenti, non vedrai nulla.
Mentre questo sarebbe sufficiente se hai un'app con uno o due utenti, non è sufficiente avere le registrazioni aperte.
Aprire il controller Utenti e aggiungere il componente ACL.
var $ components = array ('Auth', 'Acl');
Successivamente, creiamo una funzione per installare i nodi ACO e ARO.
function install () if ($ this-> Acl-> Aro-> findByAlias ("Admin")) $ this-> redirect ('/'); $ aro = new aro (); $ Aro-> creare (); $ aro-> save (array ('model' => 'Utente', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Super')); $ Aro-> creare (); $ aro-> save (array ('model' => 'User', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Admin')); $ Aro-> creare (); $ aro-> save (array ('model' => 'Utente', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Utente')); $ Aro-> creare (); $ aro-> save (array ('model' => 'Utente', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Sospeso')); $ aco = new Aco (); $ Aco-> creare (); $ aco-> save (array ('model' => 'Utente', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Utente')); $ Aco-> creare (); $ aco-> save (array ('model' => 'Post', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Post')); $ this-> Acl-> allow ('Super', 'Post', '*'); $ this-> Acl-> allow ('Super', 'User', '*'); $ this-> Acl-> allow ('Admin', 'Post', '*'); $ this-> Acl-> allow ('User', 'Post', array ('create'));
Importando il componente ACL, possiamo accedere al modello ACOs e AROs. Iniziamo creando un ARO, che è il richiedente per gli oggetti; in altre parole, chi accederà agli oggetti. Quello sarebbe l'utente (da cui la stringa User nel modello). Stiamo creando ruoli diversi; Super, Admin, User e Suspended.
Successivamente, facciamo lo stesso con gli ACO, dato che abbiamo solo due oggetti da gestire (Post e Utenti), ne creeremo due, uno per ogni.
Ora, una breve nota. L'array che stiamo salvando sia per gli ACO che per gli ARO ha quattro campi: il nome del modello, la chiave esterna (utile se vuoi dare accesso a un ARO, come un post che ha creato), id padre (che verrà usato in seguito ed è la relazione di base dei nodi padre-figlio, creeremo utenti al di sotto di questi ruoli) e l'alias (che è una forma veloce per trovare gli oggetti).
Infine, stiamo usando ACL permettere()
funzione. Il primo parametro è l'alias ACO; in secondo luogo, l'alias ARO, e in terzo luogo, le autorizzazioni concesse a detto ARO. Un superutente ha pieno accesso ai modelli Post e User, un amministratore ha pieno accesso al modello Post e un utente può semplicemente creare post.
All'inizio della funzione, ho dichiarato un condizionale per verificare se il ruolo Admin esiste negli ACO. Non vuoi installare la stessa cosa più di una volta, vero? Farebbe un po 'male il database.
Aperto / utenti / install
nel tuo browser web e, dato che non abbiamo una vista, CakePHP genera un errore, ma controlla semplicemente il dump di MySQL. Tutte le relazioni sono state create con successo, è ora di lavorare con i nodi figli.
Puliamo il utenti
tavolo. Apri phpMyAdmin, seleziona il tuo database, il utenti
tabella e fare clic su vuoto. Torneremo al Registrare()
funzione sul controller degli utenti. Appena sotto questa linea:
$ This-> Auth-> d'accesso ($ data);
Incolla questo codice:
// Imposta i ruoli utente $ aro = new Aro (); $ parent = $ aro-> findByAlias ($ this-> User-> find ('count')> 1? 'User': 'Super'); $ Aro-> creare (); $ aro-> save (array ('model' => 'User', 'foreign_key' => $ this-> User-> id, 'parent_id' => $ parent ['Aro'] ['id'], ' alias '=>' Utente :: '. $ this-> Utente-> id));
Nella prima riga creiamo un nuovo oggetto ARO. Quindi otterremo il nodo genitore in cui verrà creato l'utente. Se c'è un record nel database, lo imposteremo sull'utente ARO, altrimenti il primo utente dovrebbe essere il Super.
Quindi salveremo un nuovo ARO; il modello è quello su cui stiamo lavorando attualmente, Utente
, il foreign_key
è l'ID dell'ultimo record che abbiamo appena creato. Il parent_id
è il nodo con cui abbiamo iniziato; gli passeremo semplicemente l'id e infine l'alias. È una buona idea chiamarlo Nome del modello
, quindi un separatore ::
, e poi l'identificatore. Sarà molto più facile trovarlo.
Ora abbiamo finito. Crea quattro utenti: superuser, adminuser, normaluser e suspendeduser. Suggerisco lo stesso nome utente e password per scopi di test. Non dimenticare che, fino a questo punto, solo il superuser ha un ruolo di Super; tutti i restanti saranno Utenti!
Poiché ACL è un componente, è accessibile solo all'interno del controller. Più tardi, sarà anche nella vista; ma prima le cose prima. Includere il componente ACL nel controller Post, come abbiamo fatto nel controller Users. Ora puoi iniziare a controllare le autorizzazioni. Andiamo alla funzione di modifica e facciamo un test rapido. Nella prima riga di detto metodo, aggiungi questo.
$ user = $ this-> Auth-> user (); se (! $ this-> Acl-> check ('User ::'. $ user ['User'] ['id'], 'Post', 'update')) muore ('non sei autorizzato');
ACL dai un'occhiata()
la funzione richiede tre parametri: ARO, ACO e azione. Vai e modifica un post e, se non sei loggato come superutente, lo script morirà. Vai a / Utenti / login
e accedere come utente Super e tornare alla modifica. Dovresti essere in grado di modificare il post. Controlla sotto la discarica di MySQL per vedere la magia. Quattro query di database: quella sicuramente non è scalabile.
Abbiamo due problemi. Primo, due righe per il controllo delle autorizzazioni. In secondo luogo, gli ACL non vengono memorizzati nella cache. E non dimenticare che tutti gli utenti registrati possono vedere il link di modifica, anche se solo l'utente Super può utilizzarlo.
Creiamo un nuovo componente. Chiamiamolo Accesso.
user = $ this-> Auth-> user (); ?>
Salvalo controllori / componenti
come access.php
. La classe $ user
var verrà avviato quando il componente viene caricato e avviare()
è una funzione di callback, quindi è ora impostata nella classe. Ora creiamo il nostro dai un'occhiata()
funzione.
controllo della funzione ($ aco, $ action = '*') if (! empty ($ this-> user) && $ this-> Acl-> check ('User ::'. $ this-> user ['User' ] ['id'], $ aco, $ action)) return true; else return false;
Nostro dai un'occhiata()
il metodo richiede solo due parametri: l'ACO e l'azione (che è opzionale). L'ARO sarà l'utente corrente per ogni sessione. Il parametro di azione verrà impostato su *
, che è l'accesso completo per l'ARO. La funzione inizia verificando se il $ This-> user
non è vuoto (che in realtà ci dice se un utente ha effettuato l'accesso) e poi passiamo all'ACL. Ne abbiamo già parlato.
Ora possiamo includere il componente Access nel nostro controller Post e controllare le autorizzazioni con una sola riga.
se (! $ this-> Access-> check ('Post', 'update')) muore ('non sei autorizzato');
Lo stesso risultato si ottiene con meno codice, ma il messaggio di errore è brutto. Faresti meglio a sostituire il morire()
con il gestore di errori CakePHP:
$ This-> cakeError ( 'error404');
Questo potrebbe essere brutto, ma funziona. Dovremo creare un helper che carichi un componente con un metodo personalizzato da utilizzare nell'helper.
Aggiungi questa funzione nel componente di accesso (controllori / componenti / access.php
).
function checkHelper ($ aro, $ aco, $ action = "*") App :: import ('Component', 'Acl'); $ acl = new AclComponent (); return $ acl-> check ($ aro, $ aco, $ action);
Ora, riscriviamo l'helper di accesso (views / aiutanti / access.php
).
Access = new AccessComponent (); App :: import ('Component', 'Auth'); $ this-> Auth = new AuthComponent (); $ this-> Auth-> Session = $ this-> Session; $ this-> user = $ this-> Auth-> user (); controllo della funzione ($ aco, $ action = '*') if (vuoto ($ this-> user)) restituisce false; restituire $ this-> Access-> checkHelper ('User ::'. $ this-> user ['User'] ['id'], $ aco, $ action); function isLoggedin () return! empty ($ this-> user); ?>
Il BeforeRender ()
metodo è un callback, simile a quello del componente avviare()
. Stiamo caricando due componenti e poiché questi verranno utilizzati nella maggior parte delle funzioni, è consigliabile avviare tutto in una volta, anziché avviarli manualmente ogni volta che viene chiamato il metodo.
Ora sul tuo index.ctp
guarda dentro Visto / messaggi
puoi sostituire questa linea.
"> modifica | echo $html->link ('cancella', '/posts/delete/'.$post['Post']['id'], NULL, 'Sei sicuro?'); ?>
Con questo.
if($access->check ('Post')):?>"> modifica | echo $html->link ('cancella', '/posts/delete/'.$post['Post']['id'], NULL, 'Sei sicuro?'); ?> endif; ?>
Basta non dimenticare di controllare le autorizzazioni, sia nelle viste che nei controller!
È possibile accedere ai dati utente per un utente registrato con utente()
metodo nel componente Auth. Quindi è possibile accedere alla matrice e ottenere le informazioni desiderate. Ma ci deve essere un modo migliore. Aggiungiamo la seguente funzione nel componente di accesso.
function getmy ($ what) return! empty ($ this-> user) && isset ($ this-> user ['User'] [$ cosa])? $ this-> user ['User'] [$ what]: false;
Questo è abbastanza utile quando è necessario salvare un messaggio con un ID utente
relazione.
$ this-> data ['Post'] ['user_id'] = $ this-> Access-> getmy ('id');
E dal punto di vista, possiamo fare qualcosa di simile con l'aiuto.
function getmy ($ what) return! empty ($ this-> user) && isset ($ this-> user ['User'] [$ cosa])? $ this-> user ['User'] [$ what]: false;
Nel tuo file di modello puoi fare qualcosa di simile qui sotto per salutare un utente con il suo nome utente.
benvenuto =$access->isLoggedIn ()? $ access-> getmy ('username'): 'Guest'; ?>
Diciamo che dobbiamo cambiare ruolo per l'utente n. 4: deve essere un Super User. Quindi, l'id utente è 4 e l'id di Aro è 1.
$ user_id = 4; $ user_new_group = 1;
Ora dobbiamo trovare l'Aro dell'utente per modificare il suo Aro genitore.
$ aro_user = $ this-> Acl-> Aro-> find ('first', array ('conditions' => array ('Aro.parent_id! =' => NULL, 'Aro.model' => 'Utente', 'Aro.foreign_key' => $ user_id)));
Infine, se il $ aro_user
la variabile non è vuota, aggiorniamo il file Aro.parent_id
campo.
if (! empty ($ aro_user)) $ data ['id'] = $ aro_user ['Aro'] ['id']; $ data ['parent_id'] = $ user_new_group; $ This-> acl-> Aro-> save ($ data);
Si noti che è necessario convalidare sia l'id dell'utente che l'id del nuovo aro. Se uno di questi non esiste, creerà un problema nelle tabelle ACL.
Sebbene il componente che abbiamo appena creato sia semplice e utile, il database viene interrogato con ogni controllo. Non è ancora pronto per la produzione. Innanzitutto, il database dovrebbe essere interrogato il meno possibile. Per ottimizzare questo, dovremmo sfruttare la cache di CakePHP.
Usare Cache ridurrà un po 'il carico, ma se nell'indice ci sono dieci post, e, per ognuno, stiamo controllando se l'utente ha le autorizzazioni di aggiornamento per Post Aco, il framework leggerà e analizzerà un file per restituire lo stesso risultato ... dieci volte per ogni caricamento della pagina.
Questo è il secondo punto: una variabile all'interno della classe e alcuni condizionali renderanno il lavoro più leggero, in modo che una richiesta ripetuta controlli la cache solo una volta.
Entrambe queste modifiche si riflettono in access_cache.php
, dentro il controllori / componenti
directory. Quindi assicurati di scaricare la fonte!
Gli elenchi di controllo degli accessi sono una funzionalità di base della maggior parte delle app. CakePHP ha una buona implementazione, ma manca sia nella buona documentazione che negli esempi. Spero che, con questo tutorial, questi due problemi saranno ora trattati. Grazie per aver letto!