Source : Lior Chamla SYMFONY 4/4 : 1H POUR COMPRENDRE L'AUTHENTIFICATION !
Éditer le fichier config/packages/security.yaml
#(1)
Configurer un encoder pour l'entité App\Entity\User#(2a)
notre provider s'appelle users#(2b)
correspond à l'entité App\Entity\User#(2c)
c'est la propriété email de la classe qui identifie les utilisateurs#(3a)
pour le firewall main on utilise le provider users#(3b)
comme méthode d'authentification, on utilisera un formulaire de login#(3c)
la route menant au formulaire s'appelle login#(3d)
la route de vérification est également la route login#(3e)
route de redirection après login ⇒ voir https://symfony.com/doc/current/security/form_login.html#changing-the-default-page#(4a)
nom de la route utilisée pour le logout#(4b)
route de redirection après le logoutsecurity: encoders: App\Entity\User: algorithm: bcrypt #(1) providers: users: #(2a) entity: class: App\Entity\User #(2b) property: email #(2c) firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images\js)/ security: false main: anonymous: true provider: users #(3a) form_login: #(3b) login_path: login #(3c) check_path: login #(3d) default_target_path: menu #(3e) logout: path: logout #(4a) target: accueil #(4b)
php bin/console make:entity User
Fichiers créés :
src/Entity/User.php |
src/Repository/UserRepository.php |
Ajout des propriétés :
Nom | Type | Longueur | Nullable |
---|---|---|---|
string | 255 | Non | |
username | string | 255 | Non |
roles | simple_array | Non | |
password | string | 255 | Non |
j'ai utilisé le type simple_array
plutôt que le type json
car sur mon serveur MariaDB le type de champ json
n'était
pas encore supporté, alors qu'en MySQL il existe depuis longtemps…
Le champ dans la DB est du type longtext et son contenu doit être
constitué de chaînes séparées par des virgules, par exemple ROLE_USER,ROLE_ADMIN
.
Créer la migration :
php bin/console make:migration
Appliquer la migration :
php bin/console doctrine:migrations:migrate
On a besoin d'une propriété confirm_password
utilisée dans le
formulaire d'inscription, mais celle-ci ne doit pas être liée à un
champ de la base de données.
On l'ajoute manuellement, ainsi que les fonctions getter et setter.
Class User { // ... private $confirm_password; // ... public function getConfirmPassword(): ?string { return $this->confirm_password; } public function setConfirmPassword(string $confirm_password): self { $this->confirm_password = $confirm_password; return $this; } }
use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; /** * @ORM\Entity(repositoryClass="App\Repository\UserRepository") * @UniqueEntity( * fields = {"email"}, * message = "L'email indiqué est déjà utilisé" * ) */ Class User implements UserInterface { /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string", length=255) * @Assert\Email() */ private $email; /** * @ORM\Column(type="string", length=255) */ private $username; /** * @ORM\Column(type="string", length=255) * @Assert\Length( * min="8", * minMessage="Votre mot de passe doit comporter au minimum 8 caractères !" * ) */ private $password; /** * @Assert\Equalto( * propertyPath="password", * message="Vous n'avez pas tapé deux fois le même mot de passe !" * ) */ private $confirm_password; // Getters et setters pour les champs //*********************************** // ... // Implémentation des fonctions de UserInterface //********************************************** public function eraseCredentials() { } public function getSalt() { } public function getRoles() { return ['ROLE_USER']; } }
php bin/console make:form RegistrationType
Fichiers créés :
src/Form/RegistrationType.php |
Répondre aux questions :
Nom de l'entité | User |
Les champs de l'entité User sont déjà ajoutés dans la classe du formulaire.
use Symfony\Component\Form\Extension\Core\Type\PasswordType; class RegistrationType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('email') ->add('username') ->add('password', PasswordType::class) ->add('confirm_password', PasswordType::class) ; } }
php bin/console make:controller
Choose a name for your controller class: > SecurityController
Fichiers créés :
src/Controller/SecurityController.php |
templates/security/index.html.twig |
use App\Entity\User; use App\Form\RegistrationType; use Docrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; class SecurityController extends Controller { /** * @Route("/register", name="register") */ public function register( Request $request, EntityManagerInterface $manager, UserPasswordEncoderInterface $encoder ) { $user = new User(); $form = $this->createForm(RegistrationType::class, $user); $form->handleRequest($request); if($form->isSubmitted() && $form->isValid()) { $hash = $encoder->encodePassword($user, $user->getPassword()); $user->setPassword($hash); $manager->persist($user); $manager->flush(); return $this->redirectToRoute('login'); } return $this->render("security/register.html.twig", [ 'form' => $form->createView() ]); } /** * @Route("/login", name="login") */ public function login() { return $this->render("security/login.html.twig"); } /** * @Route("/logout", name="logout") */ public function logout() { // Cette fonction ne fait rien. // Elle est là juste pour que la route "logout" existe. } }
{% extends 'base.html.twig' %} {% block title %}Inscription{% endblock %} {% block body %} <h1>Inscription</h1> {{ form_start(form) }} {{ form_row(form.username, { 'label': "Nom d'utilisateur", 'attr': { 'placeholder': "Nom d'utilisateur" } }) }} {{ form_row(form.email, { 'label': "Adresse email", 'attr': { 'placeholder': "Adresse email" } }) }} {{ form_row(form.password, { 'label': "Mot de passe", 'attr': { 'placeholder': "Mot de passe" } }) }} {{ form_row(form.confirm_password, { 'label': "Confirmation du mot de passe", 'attr': { 'placeholder': "Confirmation du mot de passe" } }) }} <button type="submit" class="btn btn-success">Inscription</button> {{ form_end(form) }} {% endblock %}
Pour qu'ils soient traités automatiquement par le système de sécurité, les noms des champs doivent être les suivants :
_username | Nom de l'utilisateur (même si l'identifiant est un email) |
_password | Mot de passe |
_target_path | Route de redirection lorsque login ok |
{% extends 'base.html.twig' %} {% block body %} <h1>Connexion</h1> <form action="{{ path('login') }}" method="post"> <div class="form-group"> <input type="text" name="_username" class="form-control" placeholder="Adresse email" required> </div> <div class="form-group"> <input type="password" name="_password" class="form-control" placeholder="Mot de passe" required> </div> <div class="form-group"> <button type="submit" class="btn btn-success">Connexion</button> </div> </form> {% endblock %}
Pour afficher ou cacher ces options dans le menu, selon que l'utilisateur est connecté ou pas :
{% if not app.user %} <li class="nav-item"> <a href="{{ path('login') }}" class="nav-link">Connexion</a> </li> {% else %} <li class="nav-item"> <a href="{{ path('logout') }}" class="nav-link">Déconnexion</a> </li> {% endif %}