Outils pour utilisateurs

Outils du site


symfony4:authentification

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
  • 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.

User.php
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
User.php
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.

RegistrationType.php
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
SecurityController.php
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

register.html.twig
{% 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 %}

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
login.html.twig
{% 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 %}

Boutons "Sign In" / "Log In" / "Log Out"

Pour afficher ou cacher ces options dans le menu, selon que l'utilisateur est connecté ou pas :

main_menu.html.twig
{% 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 %}
symfony4/authentification.txt · Dernière modification: 2020/04/23 14:37 par marclebrun