Symfony2: Authentification à partir d'une base de données
Cet exemple ci-dessous présente comment réaliser une authentification à partir d'une base de données avec Symfony2.
Prenons les deux entités "Admin" et "Client" comme fornisseurs des utilisateurs.
Ces entités doivent implémenter les interfaces UserInterface et Serializable. Donc il faut définir les méthodes suivantes:
-
getRoles(): Retourne le role affecté à un utilisateur.
-
getPassword(): Retourne le mot de passe à utiliser lors de teste d'authentification.
-
getSalt(): Retourne une chaine de caractères à concaténer au mot de passe, si on a besoin.
-
getUsername(): Retourne le login à utiliser lors de teste d'authentification (ex: email, username,...).
-
eraseCredentials(): Supprimer les informations sensibles de l'entité.
-
- equals(): Comparer les infromations de l'utilisateur à un autre utilisateur donné.
- equals(): Comparer les infromations de l'utilisateur à un autre utilisateur donné.
namespace Hazem\SiteBundle\Entity;
... function getRoles() { return array('ROLE_ADMIN'); }
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Hazem\SiteBundle\Entity\Admin * * @auther Hazem * @ORM\Table(name="admin") * @ORM\Entity(repositoryClass="Hazem\SiteBundle\Entity\AdminRepository") * @ORM\HasLifecycleCallbacks() */ class Admin implements UserInterface, \Serializable {
function getUsername() { return $this->email; }
function getPassword() { return $this->motPasse; }
function getSalt() { return ''; }
function eraseCredentials() { $this->mot_de_passe = ''; }
function equals(UserInterface $user) { return ($user->getUsername() == $this->getUsername()); }
public function serialize() { return serialize(array($this->getUsername())); }
public function unserialize($serialized) { $arr = unserialize($serialized); $this->email = $arr[0];
} ... }
De même pour la 'repository' qui correspond à cette entity, elle doit implémenter l'interface 'UserProviderInterface'. Et donc les méthodes suivante:
- loadUserByUsername(): Recoit un nom d'utilisateur et cherche les enregistrements qui lui correspond.
- refreshUser(): Actualise les informations de l'utilisateur connecté.
- supportsClass(): Recoit un nom de classe, et précise est ce que l'utilisateur connecté a comme type cette classe ou non.
namespace Hazem\SiteBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class AdminRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username) {
$admin = $this->getEntityManager()->getRepository('HazemSiteBundle:Admin')
->findOneBy(array(
'email' => $username
));
return $admin;
}
function refreshUser(UserInterface $user) {
return $this->loadUserByUsername($user->getUsername());
} function supportsClass($class){
return true;
}
...
}
Ensuite, vous devez créer les deux actions, l'une pour afficher le formulaire d'authentification et l'autre pour recevoir les informations envoyées et qui seront traiter automatiquement par symfony.
Dans le contrôlleur suivant, j'ai implémenté ces deux actions (login et check), avec une action pour la déconnexion et une autre pour rédiriger l'utilisateur selon son rôle:
namespace Hazem\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\FormError;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @author hazem
*/
class SecuriteController extends Controller {
/**
* Afficher le formulaire d'authentification
* @Route("/login/", name="login")
* @Template()
* @param \Symfony\Component\HttpFoundation\Request $request
* @return Response
*/
public function loginAction() {
$request = $this->getRequest();
$session = $request->getSession();
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
}
if ($error)
$errorMessage = $error->getMessage();
else
$errorMessage='';
return array(
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error_message' => $errorMessage,
);
}
/**
* Action pour recevoir les informations envoyées
* @Route("/login_check/", name="login_check")
* @author hazem
* @return \Symfony\Bundle\FrameworkBundle\Controller\RedirectResponse
*/
public function checkAction(Request $request) {
return new Response();
}
/**
* Deconnexion d'un utilisateur
* @Route("/logout/", name="logout")
* @author hazem
* @return \Symfony\Bundle\FrameworkBundle\Controller\RedirectResponse
*/
public function logoutAction() {
$session = $this->getRequest()->getSession();
$token = $session->get('token');
$this->get("request")->getSession()->invalidate();
$this->get("security.context")->setToken(null);
$this->getRequest()->getSession()->remove('token');
return $this->redirect($this->generateUrl('acceuil'));
}
/**
* redirection (selon le rôle) après une authentification avec succès
* @Route("/redirection/", name="redirection")
* @author hazem
* @return \Symfony\Bundle\FrameworkBundle\Controller\RedirectResponse
*/
public function redirectionAction() {
if ($this->get('security.context')->isGranted('ROLE_ADMIN'))
return $this->redirect($this->generateUrl('admin_acceuil'));
elseif ($this->get('security.context')->isGranted('ROLE_CLIENT'))
return $this->redirect($this->generateUrl('client_acceuil'));
}
}
L'action 'login' permet d'afficher un formulaire d'authentification. Voilà un lien pour vous aider à créer un: http://symfony.com/doc/2.0/book/security.html#using-a-traditional-login-form
Maintenant, on doit configurer le système de sécurité. Dans le fichier security.yml, on doit préciser:
- Les 'encoders' :description d'encodage de mot de passe d'une entité.
- Les 'providers': les entités fournisseurs des utilisateurs.
- Les 'firewalls': Les zones où la sécuirité de symfony est activée.
- Les règles 'access_control': Association entre les urls et les droits nécessaires.
security: encoders:
entity_admin: class: Hazem\SiteBundle\Entity\Admin algorithm: sha1 iterations: 1 encode_as_base64: false entity_client: class: Hazem\SiteBundle\Entity\Client
algorithm: sha1 iterations: 1
encode_as_base64: false
providers: chain_provider: providers: [entity_admin, entity_client] entity_admin: entity: class: Hazem\SiteBundle\Entity\Admin entity_client: entity: class: Hazem\SiteBundle\Entity\Client
firewalls: secured_area: pattern: /.* anonymous: ~ form_login: check_path: /securite/login_check/ login_path: /securite/login/ username_parameter: email password_parameter: motpasse always_use_default_target_path: true default_target_path: /securite/redirection logout: path: /securite/logout/ target: /
access_control: #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
Vous voyez ici que 'login_path' et 'check_path' pointent sur les actions déjà créer dans SecuriteController.
Les deux instructions suivantes ont pour rôle de rédiriger l'utilisateur selon son rôle, soit qu'il arrive directment au page login , soit il demande un url protégé:
always_use_default_target_path: true
default_target_path: /securite/redirection
Pour sécuriser les urls, vous pouvez modifier 'access_control' dans security.yml, ou préciser le rôle nécessaire au niveau de l'action et avec les annotations, exemple:
class AdminController extends Controller
{
/**
* @Route("/", name="admin_acceuil")
* @Template()
* @Secure(roles="ROLE_ADMIN")
*/
public function indexAction()
{
$user = $this->get('security.context')->getToken()->getUser();
return array('currentUser' => $user,
'page_menu_top'=>'acceuil',
'page_sub_menu_top'=>''
);
}
... }
Vous pouvez trouvez plus d'informations sur le système de sécurité de symfony2 ici:
http://symfony.com/doc/2.0/book/security.html
http://symfony.com/doc/2.0/reference/configuration/security.html
http://symfony.com/doc/2.0/cookbook/security/form_login.html