====== 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 %}