====== Authentification ====== Source : **Lior Chamla** [[https://www.youtube.com/watch?v=_GjHWa9hQic|SYMFONY 4/4 : 1H POUR COMPRENDRE L'AUTHENTIFICATION !]] ===== Configuration de la sécurité ===== Éditer le fichier **config/packages/security.yaml** * ''#(1)'' Configurer un **encoder** pour l'entité **App\Entity\User** * Configurer un **provider** pour indiquer d'où viennent les utilisateurs * ''#(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 * Configurer un ou plusieurs **firewalls** * ''#(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]] * Logout * ''#(4a)'' nom de la route utilisée pour le logout * ''#(4b)'' route de redirection après le logout security: 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) ===== Entité USER ===== 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 ^ | email | string | 255 | Non | | username | string | 255 | Non | | roles | simple_array | | Non | | password | string | 255 | Non | ==== Note sur le champ ROLES ==== 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 ==== Ajouter une propriété "confirm_password" à l'entité ==== 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; } } ==== Contraintes et UserInterface ==== * Contrainte d'unicité de l'entité * Contraintes de validation des champs * Implémenter l'interface **UserInterface** 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']; } } ===== Formulaire d'inscription ===== 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) ; } } ===== Contrôleur ===== 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. } } ==== Templates du contrôleur ==== === Register === {% extends 'base.html.twig' %} {% block title %}Inscription{% endblock %} {% block body %}

Inscription

{{ 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" } }) }} {{ form_end(form) }} {% endblock %}
=== Login === 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 %}

Connexion

{% endblock %}
===== Boutons "Sign In" / "Log In" / "Log Out" ===== Pour afficher ou cacher ces options dans le menu, selon que l'utilisateur est connecté ou pas : {% if not app.user %} {% else %} {% endif %}