Sécuriser l'interface d'administration
L'interface d'administration ne doit être accessible que par des personnes autorisées. La sécurisation de cette zone du site peut se faire à l'aide du composant Symfony Security.
Définir une entité User
Même si les internautes ne pourront pas créer leur propre compte sur le site, nous allons créer un système d'authentification entièrement fonctionnel pour l'admin. Nous n'aurons donc qu'un seul User, l'admin du site.
La première étape consiste à définir une entité User
. Pour éviter toute confusion, nommons-la plutôt Admin
.
Pour utiliser l'entité Admin
dans le système d'authentification de Symfony, celle-ci doit respecter certaines exigences spécifiques. Par exemple, elle a besoin d'une propriété password
.
Utilisez la commande dédiée make:user
pour créer l'entité Admin
au lieu de la commande traditionnelle make:entity
:
1
$ symfony console make:user Admin
Répondez aux questions qui vous sont posées : nous voulons utiliser Doctrine pour stocker nos users (yes
), utiliser username
pour le nom d'affichage unique des admins et chaque admin aura un mot de passe (yes
).
La classe générée contient des méthodes comme getRoles()
, eraseCredentials()
et d'autres qui sont nécessaires au système d'authentification de Symfony.
Si vous voulez ajouter d'autres propriétés à l'entité Admin
, exécutez make:entity
.
Ajoutons une méthode __toString()
comme EasyAdmin les aime :
1 2 3 4 5 6 7 8 9 10 11 12 13 14
--- a/src/Entity/Admin.php
+++ b/src/Entity/Admin.php
@@ -54,6 +54,11 @@ class Admin implements UserInterface, PasswordAuthenticatedUserInterface
return (string) $this->username;
}
+ public function __toString(): string
+ {
+ return $this->username;
+ }
+
/**
* @see UserInterface
*/
En plus de générer l'entité Admin
, la commande a également mis à jour la configuration de sécurité pour connecter l'entité au système d'authentification :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -1,7 +1,15 @@
security:
+ password_hashers:
+ App\Entity\Admin:
+ algorithm: auto
+
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
- in_memory: { memory: null }
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\Admin
+ property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
Nous laissons Symfony choisir le meilleur algorithme possible pour hacher les mots de passe (il évoluera avec le temps).
Il est temps de générer une migration et de migrer la base de données :
1 2
$ symfony console make:migration
$ symfony console doctrine:migrations:migrate -n
Générer un mot de passe pour l'admin
Nous ne développerons pas de système dédié pour créer des comptes d'administration. Encore une fois, nous n'aurons qu'un seul admin. Le login sera admin
et nous devons générer le hash du mot de passe.
Sélectionnez App\Entity\Admin
et choisissez ce que vous voulez comme mot de passe et exécutez la commande suivante pour générer le hash du mot de passe :
1
$ symfony console security:hash-password
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Symfony Password Hash Utility
=============================
Type in your password to be hashed:
>
------------------ ---------------------------------------------------------------------------------------------------
Key Value
------------------ ---------------------------------------------------------------------------------------------------
Hasher used Symfony\Component\PasswordHasher\Hasher\MigratingPasswordHasher
Password hash $argon2id$v=19$m=65536,t=4,p=1$BQG+jovPcunctc30xG5PxQ$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s
------------------ ---------------------------------------------------------------------------------------------------
! [NOTE] Self-salting hasher used: the hasher generated its own built-in salt.
[OK] Password hashing succeeded
Créer un admininistrateur
Insérez l'admin grâce à une requête SQL :
1 2 3
$ symfony run psql -c "INSERT INTO admin (id, username, roles, password) \
VALUES (nextval('admin_id_seq'), 'admin', '[\"ROLE_ADMIN\"]', \
'\$argon2id\$v=19\$m=65536,t=4,p=1\$BQG+jovPcunctc30xG5PxQ\$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s')"
Notez l'échappement du caractère $
dans le mot de passe ; échappez tous les caractères qui en ont besoin !
Configurer le système d'authentification
Maintenant que nous avons un admin, nous pouvons sécuriser l'interface d'administration. Symfony accepte plusieurs stratégies d'authentification. Utilisons un classique système d'authentification par formulaire.
Exécutez la commande make:auth
pour mettre à jour la configuration de sécurité, générer un template pour la connexion et créer une classe d'authentification (authenticator) :
1
$ symfony console make:auth
Sélectionnez 1
pour générer une classe d'authentification pour le formulaire de connexion, nommez la classe d'authentification AppAuthenticator
, le contrôleur SecurityController
et créez une URL /logout
(yes
).
La commande a mis à jour la configuration de sécurité pour lier les classes générées :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -16,6 +16,13 @@ security:
security: false
main:
anonymous: lazy
+ guard:
+ authenticators:
+ - App\Security\AppAuthenticator
+ logout:
+ path: app_logout
+ # where to redirect after logout
+ # target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
Comme l'indique la sortie de la commande, nous devons personnaliser la route dans la méthode onAuthenticationSuccess()
pour rediriger l'admin lorsqu'il a réussi à se connecter :
1 2 3 4 5 6 7 8 9 10 11 12 13
--- a/src/Security/AppAuthenticator.php
+++ b/src/Security/AppAuthenticator.php
@@ -49,9 +49,7 @@ class AppAuthenticator extends AbstractLoginFormAuthenticator
return new RedirectResponse($targetPath);
}
- // For example:
- //return new RedirectResponse($this->urlGenerator->generate('some_route'));
- throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
+ return new RedirectResponse($this->urlGenerator->generate('admin'));
}
protected function getLoginUrl(Request $request): string
Tip
Comment puis-je savoir que la route d'EasyAdmin est admin
(comme spécifié dans App\Controller\Admin\DashboardController
) ? Je ne peux pas. Mais j'ai lancé la commande suivante qui montre l'association entre les noms de route et les chemins :
1
$ symfony console debug:router
Ajouter les règles de contrôle d'accès
Un système de sécurité se compose de deux parties : l'authentification et l'autorisation. Lors de la création de l'admin, nous lui avons donné le rôle ROLE_ADMIN
. Limitons la section /admin
aux seules personnes ayant ce rôle en ajoutant une règle à access_control
:
1 2 3 4 5 6 7 8 9 10 11
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -35,7 +35,7 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- # - { path: ^/admin, roles: ROLE_ADMIN }
+ - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
when@test:
Les règles access_control
limitent l'accès par des expressions régulières. Lorsqu'une personne connectée tente d'accéder à une URL qui commence par /admin
, le système de sécurité vérifie qu'elle a bien le rôle ROLE_ADMIN
.
S'authentifier avec le formulaire de connexion
Si vous essayez d'accéder à l'interface d'administration, vous devriez maintenant être redirigé vers la page de connexion et être invité à entrer un identifiant et un mot de passe :
Connectez-vous en utilisant admin
et le mot de passe que vous avez choisi précédemment. Si vous avez copié exactement ma requête SQL, le mot de passe est admin
.
Notez qu'EasyAdmin s'intègre automatiquement au système d'authentification de Symfony :
Essayez de cliquer sur le lien "Sign out". Et voilà ! Nous avons une interface d'administration entièrement sécurisée.
Note
Si vous voulez créer un système complet d'authentification par formulaire, jetez un coup d’œil à la commande make:registration-form
.