Autenticare gli utenti con Zend_Auth - 1

Introduzione

L’autenticazione degli utenti è una delle funzionalità più diffuse nelle applicazioni web. Questa operazione, comunemente chiamata login, si basa sul controllo delle credenziali fornite e non deve essere confusa con la gestione delle autorizzazioni, processo con cui si stabilisce se un utente (già identificato) possiede i diritti per accedere ad una particolare risorsa.

Lo Zend Framework offre due componenti per la gestione dell’autenticazione e delle autorizzazioni, rispettivamente lo Zend_Auth e lo Zend_Acl. In questo articolo analizzeremo i principi di funzionamento di Zend_Auth e nel successivo vedremo come realizzare un sistema di login basato su database.

E' importante tener presente da subito che l’architettura di Zend_Auth è fortemente modulare, come del resto lo è tutto il framework. Questo perché le modalità di autenticazione dell’utente possono essere molteplici, difficilmente esauribili con una rigida implementazione del componente.


Architettura di Zend_Auth

Zend_Auth è la classe principale del sistema di gestione dell’autenticazione utente dello Zend Framework. In una applicazione può esistere un solo oggetto Zend_Auth, infatti la classe implementa il Singleton pattern. Non è necessario quindi istanziare direttamente questa classe ma, quando se ne ha bisogno, basta usare Zend_Auth::getInstance() per accedere all’unica istanza, anche se nulla vieta di assegnare tale istanza ad una variabile.

Come anticipato lo Zend_Auth non è un componente monolitico ma si affida a due ulteriori classi per realizzare due importanti compiti: la verifica delle credenziali e la persistenza dell’identità dell’utente riconosciuto. La verifica delle credenziali viene realizzata da una classe detta Zend_Auth Adapter. Ciascun Adapter è dedicato ad un particolare meccanismo di autenticazione. Quelli già presenti nello Zend Framework sono:

  • Zend_Auth_Adapter_DbTable, in grado di autenticare un utente verificando le sue credenziali (username e password) all’interno di una tabella di un database.
  • Zend_Auth_Adapter_Digest, che utilizza il sistema di autenticazione nativo dell’HTTP basato sulla richiesta di nome e password (crittografata con un sistema basato su MD5)
  • Zend_Auth_Adapter_Http, un sistema che implementa quasi completamente l’autenticazione HTTP, con invio di password sia in chiaro che attraverso digest.

Se nessuno di questi Adapter fa al caso nostro, possiamo scriverne uno personalizzato, creando una classe che implementi l’interfaccia Zend_Auth_Adapter_Interface.

In generale un Adapter riceve in input le credenziali dell’utente, alcune informazioni per il setup del servizio di autenticazione (ad esempio nome della tabella del DB), e fornisce un metodo authenticate() per verificare l’identità dell’utente.

Indipendentemente dall’Adapter utilizzato, il suo metodo authenticate() restituisce un oggetto di tipo Zend_Auth_Result attraverso il quale è possibile conoscere l’esito dell’operazione, richiamando uno dei seguenti metodi:

isValid() - restituisce true se le credenziali dell’utente sono corrette

getCode() - restituisce un codice (una costante della classe Zend_Auth_Result) che permette di determinare non solo l’esito della validazione ma anche le cause di un eventuale insuccesso. Di seguito i valori possibili:

  • Zend_Auth_Result::SUCCESS: autenticazione effettuata con successo
  • Zend_Auth_Result::FAILURE: autenticazione fallita
  • Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND: username non trovato
  • Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS: trovati più risultati per l’username fornito
  • Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID: password errata
  • Zend_Auth_Result::FAILURE_UNCATEGORIZED: altro tipo di errore

getIdentity() - restituisce l’identità dell’utente utilizzata dura l’autenticazione. Generalmente si tratta dello username.

getMessages() - restituisce un array di messaggi di errore generati durante il processo di autenticazione.

Lo Zend_Auth Adapter si limita a verificare le credenziali ma non conserva il risultato dell’operazione. La persistenza dell’identità dell’utente riconosciuto viene affidata ad un ulteriore componente, lo Storage di Zend_Auth. Se non specificato diversamente si tratta dello Zend_Auth_Storage_Session, il quale utilizza Zend_Session (che a sua volta di default usa le sessioni di PHP). Anche in questo caso, come per l’Adapter, è possibile realizzare uno Storage personalizzato creando una classe che implementa l’interfaccia Zend_Auth_Storage_Interface. A differenza dello Zend_Auth Adapter che è necessario istanziare esplicitamente nel codice, lo Storage di Zend_Auth viene creato e gestito in maniera trasparente da Zend_Auth per cui nella maggior parte delle situazioni non dovremo preoccuparcene.


Utilizzo di Zend_Auth

Vediamo ora quali sono i passi principali per effettuare l’autenticazione dell’utente, verificare successivamente la sua identità ed infine effettuare il logout.

L’autenticazione dell’utente parte con la creazione ed il setup dell’Adapter adeguato. Ad esempio nel caso dello Zend_Auth_Adapter_DbTable passiamo all’Adapter alcune informazioni sulla tabella del DB contenete le credenziali degli utenti e successivamente passiamo username e password dell’utente corrente (maggiori dettagli nel prossimo articolo).

A questo punto si può procedere seguendo 2 strade:

1) Autenticazione diretta: richiamiamo il metodo authenticate() dell’Adapter e recuperiamo l’oggetto Zend_Auth_Result per verificarne l’esito. Se l’autenticazione ha successo procediamo alla memorizzazione dell’identità dell’utente nello Storage di Zend_Auth. Per farlo recuperiamo l’oggetto Storage col metodo getStorage() di Zend_Auth e poi utilizziamo il metodo write().

// Creo il mio Adapter (viene considerato un generico Adapter personalizzato)
$authAdapter = new MyAuthAdapter($username, $password);

// Effettuo l'autenticazione direttamente
$result = $authAdapter->authenticate();

if ($result->isValid()) {
   // Autenticazione avvenuta con successo, devo memorizzare l'esito
   Zend_Auth::getInstance()->getStorage()->write($username);
} else {
   // Autenticazione fallita
}

2) Autenticazione indiretta: richiamiamo Zend_Auth::authenticate() a cui passiamo come parametro proprio l’Adapter precedentemente creato. Zend_Auth::authenticate() chiamerà a sua volta il metodo authenticate() dell’Adapter e restituirà un oggetto Zend_Auth_Result (analogamente a quanto avviene al punto 1). Inoltre, se l’autenticazione ha esito positivo, memorizzerà l’identità dell’utente nello Storage in modo da rendere permanente il risultato dell’autenticazione.

$auth = Zend_Auth::getInstance();

// Creo il mio Adapter (viene considerato un generico Adapter personalizzato)
$authAdapter = new MyAuthAdapter($username, $password);

// Effettuo l'autenticazione indirettamente, attraverso Zend_Auth
$result = $auth->authenticate($authAdapter);

if ($result->isValid()) {
   // Autenticazione avvenuta con successo
} else {
   // Autenticazione fallita
}

Anche se a prima vista l’autenticazione indiretta sembra più conveniente, per via della memorizzazione automatica dell’esito (l’identità dell’utente loggato), il primo metodo è da preferire quando vogliamo procedere personalmente alla memorizzazione di alcuni dati dell’utente nello Storage di Zend_Auth, come vedremo nell’esempio che svilupperemo nel prossimo articolo.

Effettuata l’autenticazione e memorizzato l’esito, per verificare se l’utente corrente è loggato basta controllare se la sua identità sia presente nello Storage di Zend_Auth ed eventualmente recuperarla:

$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
   // recupero l'identità dell'utente
   $identity = $auth->getIdentity();
}

Anche se queste operazioni vengono effettuate interrogando Zend_Auth, in realtà questo componente interroga lo Storage per ottenere le informazioni necessarie.

L’operazione di logout avviene invece cancellando dallo Storage l’identità precedentemente memorizzata, ed anche in questo caso possiamo demandare l’operazione a Zend_Auth:

Zend_Auth::getInstance()->clearIdentity();