Gerer l’authentification avec Zend_Auth du Zend Framework

Comment faire un système de login sur son application ? On va voir comment mettre en place un système de connexion d’utilisateur dans son application avec le Zend Framework. Pour ce faire on va utiliser le composant Zend_Auth. Il va falloir commencer par faire le formulaire de login qui n’est pas très compliqué. Faire la validation de ce formulaire puis si il est valide s’occuper de la partie Zend_Auth ou authentification. Enfin nous verrons comment faire un petit Zend_View_Helper (aide de vue) pour créer automatiquement des liens (« login» ou « logout »)
Sommaire :
Création du formulaire de login :
On va allé assez vite sur la création du formulaire dans la mesure ou j’explique dans un autre tutoriel comment faire un formulaire.
class Model_Form_User_Login extends Zend_Form {
public function init(){
$this->setName('add_user');
$email = new Model_Form_EText('email','Adresse Email : ');
$password = new Zend_Form_Element_Password('password');
$password->setLabel('Mot de pase : ')
->setRequired(true)
->addFilter('StripTags')
->addFilter('StringTrim');
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id', 'submitbutton')
->setLabel('Se connecter');
$elements = array($email,$password, $submit);
$this->addElements($elements);
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')),
array('Errors', array('placement' => 'apend')),
'Form'
));
}
}
On voit qu’on utilise un champ email et un champ password et bien sur un bouton valider. Si la classe de l’email vous parait bizarre je vous conseil de lire le tutoriel sur les formulaires. Enfin les dernières lignes concernent la mise en forme du formulaire ainsi que la position des messages d’erreurs qui seront envoyés (ajoutés) par le modèle pour la vérification du couple Email / Password.
Le controller
Le controller est dans la classe LoginController et l’action que nous allons utiliser est loginAction. On commence par donner une instance de notre formulaire à la vue pour qu’elle puisse l’afficher.
$form = new Model_Form_User_Login(); $this->view->formLogin = $form;
Ensuite on vérifie qu’il y ai des données postées, on les récupère et on les valide.
if ($this->_request->isPost ()) {
$formData = $this->_request->getPost ();
if ($form->isValid ( $formData )) {
Si elles sont valides, on récupère les données :
$email = $form->getValue ( 'email' ); $password = $form->getValue ( 'password' );
Enfin on arrive à la partie qui concerne Zend_Auth. Il faut commencer par créer ce qu’on appel un Zend_Auth_Adapter_DbTable, c’est un composant de Zend_Auth qui va pouvoir se connecter à la BDD. Cet nouvel objet va prendre en paramètre un Zend_Db_Adapter_Abstract, c’est à dire un connecteur à la base de donnée. Dans notre cas nous allons prendre le connecteur principal, celui qui est définit dans les paramètres globaux de Zend (application.ini)
$authAdapter = new Zend_Auth_Adapter_DbTable ( Zend_Db_Table::getDefaultAdapter () );
Enfin il faut donner quelques informations à cet adaptateur :
- Le nom de la table qui contient les utilisateurs (ici users)
- Le nom de la colonne qui contient les identifiants (ici email)
- Le nom de la colonne qui contient les mots de passe (ici password)
- Le type de hashage dans la base (ici MD5)
- La valeur de l’email
- La valeur du mot de passe
$authAdapter->setTableName ( 'users' ) ->setIdentityColumn ( 'email' ) ->setCredentialColumn ( 'password' ) ->setCredentialTreatment ( 'MD5(?)' ) ->setIdentity ( $email ) ->setCredential ( $password );
Enfin on essaye d’identifier l’utilisateur :
$authAuthenticate = $authAdapter->authenticate ();
Maintenant il faut regarder si cette authentification à réussi ou échoué.
if ($authAuthenticate->isValid ()) {
Si elle est valide, on va mettre en variable de session les informations de l’utilisateur. À ce moment vous pouvez mettre toutes les informations relatives à l’utilisateur. On peu même envisager de créer une classe pour stocker toutes ces informations. Dans le cas présent, seule les informations issu de la table users sont utiles. On commence donc par récupérer l’espace de stockage (storage) par défaut de l’application
$storage = Zend_Auth::getInstance ()->getStorage ();
Puis on y ajoute les informations de l’utilisateur, on y enlève bien sur le mot de passe :
$storage->write ( $authAdapter->getResultRowObject ( null, 'password' ) );
Et enfin on redirige l’utilisateur sur la page principale de l’application
$this->_helper->redirector ( 'index', 'index' );
Et pour finir si le couple login / password n’était pas bon, on ajoute une description au formulaire :
} else {
$form->addError ( 'Il n\'existe pas d\'utilisateur avec ce mot de passe' );
}
Voilà l’action du controller est faite on peu passer à la suite.
Déconnexion
Si l’utilisateur veut se déconnecter, il doit cliquer sur un lien qui map l’action suivante :
public function logoutAction() {
Zend_Auth::getInstance ()->clearIdentity (); $this->helper->redirector ( 'index', 'index' ); }
Grâce à la ligne Zend_Auth::getInstance ()->clearIdentity (); on supprime l’identification de l’utilisateur.
Vérification de connexion
Il faut bien entendu vérifier (si l’on code proprement) si l’utilisateur est déjà connecté si il veut se déconnecter, et il faut aussi vérifier que l’utilisateur connecté puisse seulement se déconnecter dans ce controller. On va utiliser la méthode preDispatch du controller, cette méthode est systématiquement appelé à l’exécution d’une action du controller. Je vous donne le code et je l’explique après :
public function preDispatch() {
if (Zend_Auth::getInstance ()->hasIdentity ()) {
if ('logout' != $this->getRequest ()->getActionName ()) {
$this->_helper->redirector ( 'index', 'index' );
}
} else {
if ('logout' == $this->getRequest ()->getActionName ()) {
$this->_helper->redirector ( 'index' );
}
}
}
On commence par vérifier si l’utilisateur est connecté, si il l’est et que l’action sur laquelle il veut aller est différente de ‘logout’ on le renvoi sur la page d’accueil du site. Sinon, si il n’est pas connecté et qu’il veut se déconnecter, on l’envoi sur l’action qui permet de se connecter.
Zend_View_Helper
Comment faire automatiquement un lien qui permet de se connecter si on ne l’est pas, ou alors de se deconnecter si on l’est ? Et bien on utilise ce qu’on appel un Zend_View_Helper, c’est un bout de code qui va être utilisable dans le layout.
class Zend_View_Helper_ProfileLink extends Zend_View_Helper_Abstract {
public function profileLink() {
$helperUrl = new Zend_View_Helper_Url ( );
$auth = Zend_Auth::getInstance ();
if ($auth->hasIdentity ()) {
$username = $auth->getIdentity ()->prenom . ' ' . strtoupper ( substr ( $auth->getIdentity ()->nom, 0, 1 ) ) . '.';
$logoutLink = $helperUrl->url ( array ('action' => 'logout', 'controller' => 'login' ) );
return 'Salut ' . $username . ' (<a href="' . $logoutLink . '">Logout</a>)';
}
$loginLink = $helperUrl->url ( array ('action' => 'login', 'controller' => 'login' ) );
return '<a href="' . $loginLink . '">Login</a>';
}
}
On commence par créer un nouveau Zend_View_Helper_Url pour nous aider à construire les URL, puis on récupère une instance de Zend_Auth. Donc si il y a un utilisateur de connecté, on récupère son identité. Puis on construit l’URL de déconnexion, et enfin on retourne la chaine de caractère (message + lien). Si l’utilisateur n’est pas connecté, on construit le lien de connexion, et on retourne la chaine de caractère.
Si on veut utiliser ce script dans la vue, il va falloir faire :
echo $this->profileLink();
Et voila le code du controller en entier :
< ?php
class LoginController extends Zend_Controller_Action {
public function init() {
/* Initialize action controller here */
Zend_Auth::getInstance ()->clearIdentity ();
}
public function preDispatch() {
if (Zend_Auth::getInstance ()->hasIdentity ()) {
if ('logout' != $this->getRequest ()->getActionName ()) {
$this->_helper->redirector ( 'index', 'index' );
}
} else {
if ('logout' == $this->getRequest ()->getActionName ()) {
$this->_helper->redirector ( 'index' );
}
}
}
public function indexAction() {
$this->_forward ( 'login' );
}
public function loginAction() {
$form = new Model_Form_User_Login ( );
$this->view->formLogin = $form;
if ($this->_request->isPost ()) {
$formData = $this->_request->getPost ();
if ($form->isValid ( $formData )) {
$email = $form->getValue ( 'email' );
$password = $form->getValue ( 'password' );
$authAdapter = new Zend_Auth_Adapter_DbTable ( Zend_Db_Table::getDefaultAdapter () );
$authAdapter->setTableName ( 'users' )
->setIdentityColumn ( 'email' )
->setCredentialColumn ( 'password' )
->setCredentialTreatment ( 'MD5(?)' )
->setIdentity ( $email )
->setCredential ( $password );
$authAuthenticate = $authAdapter->authenticate ();
if ($authAuthenticate->isValid ()) {
$storage = Zend_Auth::getInstance ()->getStorage ();
$storage->write ( $authAdapter->getResultRowObject ( null, 'password' ) );
$this->_helper->redirector ( 'index', 'index' );
} else {
$form->addError ( 'Il n\'existe pas d\'utilisateur avec ce mot de passe' );
}
}
}
$this->render ( 'index' );
}
public function logoutAction() {
Zend_Auth::getInstance ()->clearIdentity ();
$this->_helper->redirector ( 'index', 'index' );
}
}
Articles en rapport :
- Zend Framework, Formulaire et Base de donnée, partie 2
- Zend Framework, Formulaire et Base de donnée, partie 1
- Css, JavaScript et Zend Framework
- Comment gérer une base de donnée avec le Zend Frameworkls
- Zend : Qu’est ce qu’un controller ?
{post} comment gerer l’authentification dans le #zend framework http://bit.ly/77qRr5 #zf #zend_auth
This comment was originally posted on Twitter
Pour le formulaire il y a une méthode addError ou un truc du genre. Ce n’est pas valable de faire setDescription pour un message d’erreur.
. Une fois le service créer il peut être réutilisé pour s’authentifier sur une version mobile ou un endpoint AMF
Ensuite la manipulation du singleton Zend_Auth et de l’auth adapter devrait se faire non pas dans le controlleur mais dans un service (un dossier services est créer avec zend_tool quand on génère un projet – il faut bien l’utiliser
ok, pour le addError, c’est facile a corriger, je vais faire ça de suite ! Par contre pour la deuxième partie, tu as raison, je vais me pencher la dessus. En plus ça a l’air sympa
Par contre je n’ai pas eu de dossier service avec Zend_Tool. Ne vaut il mieux pas faire un objet metier ? (un model)
Et pour ma toute dernière question, as-tu une réponse ?
Merci
Oui apparement il n’est plus créer avec Zend_Tool en revanche quand tu bootstrap ton appli avec Zend_Application l’autoloader ne nécessite pas de configuration supplémentaire pour le support d’un dossier de services (http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Application/Module/Autoloader.php)
Pour ton soucis il s’agit surement d’un problème d’autoload. Je ne peux pas trop t’aider vu que je n’utilise pas la structure par défaut de ZF et Zend_Application…
Merci beaucoup pour tes réponses
La je suis en train tester 2 3 trucs sur mon nouveau serveur, et je test ca.
Hello,
je pense qu’il doit man quer un bout de code car dans le formulaire tu as la variable $redirect mais rien qui s’y rapporte et plus loin (dans le contrôleur) tu as le redirect_url qui est instancié mais non utilisé ….
Pour l’autoload tu as toujours la possibilité de faire ceci :
dans le bootstrap,d ans la méthode protected function _initAutoload() (qui retourne l’autoload)
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(‘My’);
du coup si dans ton dossier library tu créés un dossier My (et les sous dossier qui vont avec) alors se sera charger tout seul ….
Rebonjour,
pour les helper,
si tu utilises une aide de vue (un view_helper) dans la vue et/ou dans le layouts
tu dois créer un classe ‘Zend_View_Helper_ProfileLink’ que tu mettras dans applications/views/helpers/ProfileLink.php
et puis il suffira de faire dans le layout ou dans la vue echo $this->profileLink();
@grummfy Merci, j’ai corrigé mes fautes sur le redirect url … Sinon pour ta deuxième réponse, j’ai pas tester, je verrai ce que ca donne.
Enfin pour ta troisième réponse, c’est ce que j’avais essayé de faire en premier, mais ca ne marche pas :s, il n’arrive pas a chercher la classe :S
{post} : Gerer l’authentification avec Zend_Auth du Zend… http://goo.gl/fb/7uPM #zendframework #layout #view
This comment was originally posted on Twitter
Peut-être que utilisant un module il faut que tu mettes le nom du module ou quelque chose du genre ….
mwé, je sais pas du tout, j’ai meme tester en ne mettant pas le fichier dans le module, directement dans applications/views/helpers/, mais ca ne fonctionne pas non plus :S
A partir du moment où tu le nommes Zend_View_Helper_Machintruc et que le nom du fichier est Machintruc.php et est dans le répertoire applications/views/helpers/ normalement c’est charger tout seul (et en cas d’erreur dans l’exception il te donne les répertoire de chargement et peut-être (à moins que cela ne soit via un priont_r()) les prefix des helpers qui vont avec…
En tout cas pour moi cela fonctionne … MAIS je n’ai pas de modules …
Ahlalalaa, lol, c’est bon j’ai trouver mon erreur ! un petit E en trop (ou pas) dans le nom de la classe (ProfileLink) Voila, c’est résolu, je mets a jour l’article. Merci de vos conseils
j’ai essayé de creer une pile d’erreurs avec adderror et addErrorMessage.
Qaund je dump $form->getErrors, il me retourne bien ce que je lui ai assigné mais impossible de les afficher.
merci pour ce tuto
pour afficher t’es erreurs, cela se fait tout seul… du moment que tu fait $this->view->form = $form;
$form->populate($formData);
c’est bon…
Regarde la fin du code du formulaire : http://blog.lyrixx.info/zend/gerer-lauthentification-avec-zend_auth-du-zend-framework/#formulaire
Il faut peut être que définisse le décorateur.
Sinon pour ma part, j’utilise plus la description que les erreurs, en effet avec les erreurs j’avais trop de choses qui s’affichaient :S
J’arrive enfin à faire marcher ma première appli grâce aux différents tutorials du site.
Voilà ma petite question du soir. Lorsque je fais l’authetification, je fais une redirection conditionnelle en fonction du check de l’utilisateur. Tout se passe bien, mais si je passe à une autre page du site (en changeant de controller ou juste d’action), je perds l’authetification.
Est-ce normal? Dois passer la valeur de $storage dans le registre?
Merci pour votre aide.
normalement tu n’as pas du tout a passer les données de l’auth dans les autres pages du site. une auth dure le temps d’une connexion apache. donc normalement tu n’as pas a tant soucié…. De plus, tu n’as pas à la mettre dans un registre… l’auth reste le temps de la session. elle est autonome… Tu vois ce que je veux dire ?
Merci Greg,
C’est ce que je pensais aussi… mais je n’ai pas le comportement espéré.
Mon action index ressemble à ça :
function indexAction()
{
$this->view->selectedIndex = « »;
$this->view->selectedCompte = « current »;
$this->view->selectedAbout = « »;
$this->view->selectedFAQ = « »;
$auth = Zend_Auth::getInstance ();
if ($auth->hasIdentity ()) {
$this->_redirect(‘/index/secure’);
}
else {
$this->_redirect(‘/compte/login’);
}
}
Dans l’action de login je renvoie vers cette page un fois identifié :
function loginAction()
{
$this->view->title = « Se connecter à mon compte »;
$this->view->selectedIndex = « »;
$this->view->selectedCompte = « current »;
$this->view->selectedAbout = « »;
$this->view->selectedFAQ = « »;
$this->view->formLogin = $formLogin = new Login_Form_Login;
if ($this->_request->isPost ()) {
$formData = $this->_request->getPost ();
if ($formLogin->isValid ( $formData )) {
$this->view->title = « Vous voici connecté à voter compte »;
$login = $formLogin->getValue ( ‘login’ );
$password = $formLogin->getValue ( ‘password’ );
$authAdapter = new Zend_Auth_Adapter_DbTable ( Zend_Db_Table::getDefaultAdapter () );
$authAdapter->setTableName ( ‘Tabonne’ )
->setIdentityColumn ( ‘pseudo’ )
->setCredentialColumn ( ‘password’ )
->setCredentialTreatment ( ‘MD5(?)’ )
->setIdentity ( $login )
->setCredential ( $password );
// on identifie l’utilisateur
$authAuthenticate = $authAdapter->authenticate ();
if ($authAuthenticate->isValid ()) {
$storage = Zend_Auth::getInstance ()->getStorage ();
$storage->write ( $authAdapter->getResultRowObject ( null, ‘password’ ) );
$this->_redirect(‘/compte/index’);
} else {
$formLogin->addError ( ‘Il n\’existe pas d\’utilisateur avec ce mot de passe’ );
}
} else {
$this->view->title = « Connectez vous à votre compte »;
}
}
Une fois loggé, j’ai bien la redirection vers l’action index, et le helper que j’ai mis en place donne le nom de l’utilisateur et un lien pour se déconnecter. Mais si je clique pour passé sur un autre controller, je perds l’authetification…
Est-ce que ça peut venir de la façon dont je déclare les sessions dans le bootstrap?
La je ne vois rien de foncièrement anormale. Cependant j’ai pas tout bien étudié.
Oui ca peut venir de ton bootstrap. Mais je ne vois pas ce que tu aurait pu mettre de « bizarre » …
Essaye de poser ta question sur le forom FR de Zend : http://www.z-f.fr
Merci Greg,
J’ai posé la question sur le forum dont tu parles (mon nom d’utilisateur sur le forum est mymt), mais pour le moment pas de réponse qui me permette de me dépatouiller..
Est ce que lorsque je m’identifie, je suis sensé voir un update du fichier de session sur mon serveur? Parce que pour le moment ce n’est pas le cas..
Comment ca le fichier de sessions ? tes sessions ne sont pas dans la ram de ton serveur mais dans un fichier sur le disque dur ? En tout cas si tu as un fichier, alors oui il est sensé etre modifier. A mon avis tu dois avoir un problème avec ton storage.
dans ton boot strap il faut que tu initialise comme ca :
protected function _initSession() {
// On initialise la session
$session = new Zend_Session_Namespace ( ‘Session’, true );
return $session;
}
salut tout le monde je viens de faire le tuto mais j’arrive pas a comprendre comment on appel le loginController
tu l’appel en allant sur la page correspondant a ton controller / action depuis ton navigateur
si ton controller = login et l’action = login2,
tu vas sur http://*******/login/login2/
Voila
Ok donc ce tuto n’est pas fait pour retrouver la form login dans toutes t’es page? Est ce que tu peut m’envoyer un mail pour de plus ample explication?
Si si tu peux retrouver ton form sur toutes tes pages grace au zend view helper. C’est le dernier paragraphe
bon je crois avoir compris, j’ai fais une petite mixture mais en fait il faut écrire la vérifiction de connexion dans tous tes controller?
Dans ton tuto loginController n’est appelé que lorsque l’utilisateur demande a se loguer? Si c’est le cas alors faut il créer une form de connexion ?
De plus, lorsque le viewhelper me retourne quelque chose il manque les balise qu’il vire je ne sait pas pourquoi.
ce que je veut dire c’est que ce tuto fait un lien sur toute tes page avec login et logout pas la form de connexion directement?
@Sishare :
en fait sur toutes les pages tu as la possibilité de te logguer et de te delogguer. Après tu mes les liens que tu veux dans ton view helper. Ensuite tu ajoute ton view helper a ton layout. Enfin ton controller est appelé si la personne clique sur un lien du view helper. En cliquant sur le lien « se logguer », l’utilisateur ira sur le controller loginController et obtiendra un formulaire. Si les données sont bonnes, l’utilisateur sera connecter.
Donc oui le tuto fait juste un lien. Mais dans ton view helper tu peux y mettre un form si tu veux.
tu n’as pas besoin de controller sur toutes les pages si ton utilisateur est loggué, le view helper le fait pour toi. SAUF si tu veux faire des regles d’accès, la il te faudra utiliser Zend_ACL (surement le prochain tuto !)