[Symfony 5] Autorisation distincte pour les administrateurs et les utilisateurs avec deux entités et formulaires de connexion différents

objectif



Créez une autorisation distincte dans Symfony 5:



  • Admin - aura une entité admin , une URL pour login / admin / login
  • Utilisateur - aura une entité Utilisateur , une URL pour la connexion / connexion
  • Les données de connexion ne doivent pas se chevaucher, nous ne pouvons pas nous connecter en tant qu'utilisateur sur la page / admin / login
  • Deux entités différentes doivent être créées
  • Deux contrôleurs différents doivent être créés pour la connexion et deux systèmes de sécurité différents
  • Possibilité de configurer le transfert après autorisation séparément les uns des autres
  • La possibilité d'utiliser différentes données d'autorisation (par exemple, pour l'utilisateur, nous voulons que les utilisateurs saisissent un e-mail / mot de passe, et pour que l'administrateur fournisse une protection supplémentaire en ajoutant une sorte d'Uuid


Pourquoi ce guide est-il nécessaire?



Ma tâche consistait à diviser le formulaire de connexion avec l'entité Utilisateur en deux différents - pour l'utilisateur (entité Entité) et pour l'administrateur (entité Administrateur) pour la fonctionnalité normale du panneau d'administration (dans ce cas EasyAdmin).



Dans ce tutoriel, je vais peindre étape par étape tout le chemin, en commençant par l'installation du framework lui-même et en terminant par la création de deux formes d'autorisation différentes.



Caractéristiques



  • Windows 10
  • OpenServer 5.3.7
  • PHP 7.4
  • MariaDB-10.2.12
  • Symfony 5.1


Le tutoriel est pertinent fin juin 2020.



Étape 0 - Installez Symfony 5



Nous supposerons que vous avez installé tous les composants requis, y compris Composer, dans le répertoire racine d'OpenServer (... / domaines).



composer create-project symfony/website-skeleton auth_project




Étape 1 - Configurer la base de données



Créez une nouvelle base de données, nommez-la auth_project, laissez le mot de passe et l'utilisateur être mysql. Nous devons maintenant redéfinir les paramètres .env.



Cela devrait ressembler à ceci:




# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
#  * .env                contains default values for the environment variables needed by the app
#  * .env.local          uncommitted file with local overrides
#  * .env.$APP_ENV       committed environment-specific defaults
#  * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration

###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=16cbb669c87ff9259c522ee2846cb397
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
###< symfony/framework-bundle ###

###> symfony/mailer ###
# MAILER_DSN=smtp://localhost
###< symfony/mailer ###

###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://mysql:mysql@127.0.0.1:3306/auth_project?serverVersion=mariadb-10.2.12
###< doctrine/doctrine-bundle ###



Étape 2 - Création de l'entité utilisateur



Créez une entité utilisateur, sélectionnez l'e-mail comme valeur unique



php bin/console make:user


image



Étape 3 - Créer une entité d'administration



Nous répétons tout ce qui est décrit à l'étape précédente, au lieu du nom de l'entité utilisateur que nous avons définie Admin



Étape 4 - Préparez les luminaires



Créons 2 comptes de test, un pour l'utilisateur et un pour l'administrateur. Utilisons DoctrineFixturesBundle



D'abord, vous devez l'installer



composer require --dev orm-fixtures


Après l'installation, le dossier DataFixtures apparaîtra dans / src, dans lequel le fichier AppFixtures.php sera déjà créé.



Renommez-le en UserFixtures.php et ajoutez-y les fonctionnalités nécessaires



<?php

namespace App\DataFixtures;

use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class UserFixtures extends Fixture
{
    private $encoder;

    private $em;

    public function __construct(UserPasswordEncoderInterface $encoder, EntityManagerInterface $entityManager)
    {
        $this->encoder = $encoder;
        $this->em = $entityManager;
    }

    public function load(\Doctrine\Persistence\ObjectManager $manager)
    {
        $usersData = [
              0 => [
                  'email' => 'user@example.com',
                  'role' => ['ROLE_USER'],
                  'password' => 123654
              ]
        ];

        foreach ($usersData as $user) {
            $newUser = new User();
            $newUser->setEmail($user['email']);
            $newUser->setPassword($this->encoder->encodePassword($newUser, $user['password']));
            $newUser->setRoles($user['role']);
            $this->em->persist($newUser);
        }

        $this->em->flush();
    }
}


La même chose doit être faite pour l'administrateur - créez AdminFixtures.php



<?php

namespace App\DataFixtures;

use App\Entity\Admin;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class AdminFixtures extends Fixture
{
    private $encoder;

    private $em;

    public function __construct(UserPasswordEncoderInterface $encoder, EntityManagerInterface $entityManager)
    {
        $this->encoder = $encoder;
        $this->em = $entityManager;
    }

    public function load(\Doctrine\Persistence\ObjectManager $manager)
    {
        $adminsData = [
              0 => [
                  'email' => 'admin@example.com',
                  'role' => ['ROLE_ADMIN'],
                  'password' => 123654
              ]
        ];

        foreach ($adminsData as $admin) {
            $newAdmin = new Admin();
            $newAdmin->setEmail($admin['email']);
            $newAdmin->setPassword($this->encoder->encodePassword($newAdmin, $admin['password']));
            $newAdmin->setRoles($admin['role']);
            $this->em->persist($newAdmin);
        }

        $this->em->flush();
    }
}


Étape 5 - Chargez les migrations et les fixtures dans la base de données



Les entités sont créées, nous avons enregistré les luminaires, il reste maintenant à tout remplir dans la base de données, les prochaines étapes que je réalise à chaque changement d'entités ou de luminaires




php bin/console doctrine:schema:drop --full-database --force #  ,   

php bin/console doctrine:migrations:diff #   .       !

php bin/console doctrine:migrations:migrate #     
php bin/console doctrine:fixtures:load #     


Étape 6 - Créer une autorisation



Dans la console, nous écrivons



php bin/console make:auth


Les paramètres et les noms sont les suivants:




# php bin/console make:auth

 What style of authentication do you want? [Empty authenticator]:
  [0] Empty authenticator
  [1] Login form authenticator
 > 1

 The class name of the authenticator to create (e.g. AppCustomAuthenticator):
 > UserAuthenticator

 Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
 > UserAuthSecurityController

 Do you want to generate a '/logout' URL? (yes/no) [yes]:
 >

 created: src/Security/UserAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/UserAuthSecurityController.php
 created: templates/security/login.html.twig

  Success!

 Next:
 - Customize your new authenticator.
 - Finish the redirect "TODO" in the App\Security\UserAuthenticator::onAuthenticationSuccess() method.
 - Review & adapt the login template: templates/security/login.html.twig.


En conséquence, nous mettrons à jour security.yaml et créerons 3 fichiers



Étape 7 - Modifier security.yaml



Une fois l'autorisation créée, security.yaml ressemble à ceci:




security:
    encoders:
        App\Entity\User:
            algorithm: auto
        App\Entity\Admin:
            algorithm: auto


    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\Admin
                property: email
        # used to reload user from session & other features (e.g. switch_user)
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            lazy: true
            provider: app_user_provider
            guard:
                authenticators:
                    - App\Security\UserAuthenticator
            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

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true

    # 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: ^/profile, roles: ROLE_USER }



Nous devons ajouter un nouveau fournisseur admin_user_provider et modifier les paramètres des pare-feu .



Enfin , le fichier security.yaml devrait ressembler à ceci:




security:
    encoders:
        App\Entity\User:
            algorithm: auto
        App\Entity\Admin:
            algorithm: auto


    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
        app_admin_provider:
            entity:
                class: App\Entity\Admin
                property: email
        # used to reload user from session & other features (e.g. switch_user)
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        admin_secured_area:
            pattern:   ^/admin
            anonymous: ~
            provider: app_admin_provider
            form_login:
                login_path: /admin/login
                check_path: /admin/login_check
                default_target_path: /admin/login
                username_parameter: email
                password_parameter: password
            guard:
                authenticators:
                    - App\Security\AdminAuthenticator
            logout:
                path: app_logout
                # where to redirect after logout
                target: /admin/login

        user_secured_area:
            pattern:   ^/
            anonymous: ~
            provider: app_user_provider
            form_login:
                login_path: /login
                check_path: /login_check
                default_target_path: /login
                username_parameter: email
                password_parameter: password
            logout:
                path: app_logout
                # where to redirect after logout
                target: /login
                
    # 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: ^/profile, roles: ROLE_USER }



Étape 8 - Renommez le modèle login.html.twig



Cela doit être fait, car nous allons recréer l'autorisation via make: auth.

Appelons ce fichier.



Étape 9 - Modification de UserAuthSecurityController



Le fichier se trouve sur le chemin App \ Controller, puisque nous avons changé le nom du modèle, cela doit être changé dans le contrôleur.



Qu'est-ce qu'un contrôleur devrait être:




<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class UserAuthSecurityController extends AbstractController
{
    /**
     * @Route("/login", name="app_login")
     */
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // if ($this->getUser()) {
        //     return $this->redirectToRoute('target_path');
        // }

        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('security/user-login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
    }

    /**
     * @Route("/logout", name="app_logout")
     */
    public function logout()
    {
        throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
    }
}


Étape 10 - Création d'une deuxième autorisation



Nous écrivons dans la console:




php bin/console make:auth


Puisque nous avons ajouté un nouveau fournisseur_admin_application , il nous sera proposé de choisir le pare-feu que nous voulons mettre à jour:



image



Après avoir choisi un pare-feu, proposez de sélectionner Entité, sélectionnez \ App \ Entité \ Admin:



image



Étape 11 - Renommez le login.html.twig que nous venons de créer



Renommez le login.html.twig nouvellement créé en admin-login.html.twig



Étape 12 - Modification de l'AdminAuthController que nous venons de créer



Modifier l'itinéraire et le nom du modèle:




<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class AdminAuthController extends AbstractController
{
    /**
     * @Route("/admin/login", name="app_admin_login")
     */
    public function adminLogin(AuthenticationUtils $authenticationUtils): Response
    {
        // if ($this->getUser()) {
        //     return $this->redirectToRoute('target_path');
        // }

        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('security/admin-login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
    }

    /**
     * @Route("/logout", name="app_logout")
     */
    public function logout()
    {
       throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
    }
}


Étape 13 - Modification du fichier config / routes.yaml



Créez login_check et admin_login_check, que nous avons définis dans les paramètres du pare-feu dans le



fichier config / packages / security.yaml . A quoi le fichier config / routes.yaml devrait ressembler:




#index:
#    path: /
#    controller: App\Controller\DefaultController::index
login_check:
  path: /login_check
admin_login_check:
  path: /admin/login_check



Étape 14 - Modifiez le fichier templates / secutiry / user-login.html.twig



Ajoutez l'attribut d'action à la balise:



{% extends 'base.html.twig' %}

{% block title %}Log in!{% endblock %}

{% block body %}
<form action="{{ path('login_check') }}" method="post">
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    {% if app.user %}
        <div class="mb-3">
            You are logged in as {{ app.user.username }}, <a href="{{ path('app_logout') }}">Logout</a>
        </div>
    {% endif %}

    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
    <label for="inputEmail">Email</label>
    <input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" required autofocus>
    <label for="inputPassword">Password</label>
    <input type="password" name="password" id="inputPassword" class="form-control" required>

    <input type="hidden" name="_csrf_token"
           value="{{ csrf_token('authenticate') }}"
    >

    {#
        Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
        See https://symfony.com/doc/current/security/remember_me.html

        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" name="_remember_me"> Remember me
            </label>
        </div>
    #}

    <button class="btn btn-lg btn-primary" type="submit">
        Sign in
    </button>
</form>
{% endblock %}



Étape 15 - Modifiez le fichier templates / secutiry / admin-login.html.twig



Ajoutez l'attribut d'action à la balise:



{% extends 'base.html.twig' %}

{% block title %}Log in!{% endblock %}

{% block body %}
<form action="{{ path('admin_login_check') }}" method="post">
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    {% if app.user %}
        <div class="mb-3">
            You are logged in as {{ app.user.username }}, <a href="{{ path('app_logout') }}">Logout</a>
        </div>
    {% endif %}

    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
    <label for="inputEmail">Email</label>
    <input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" required autofocus>
    <label for="inputPassword">Password</label>
    <input type="password" name="password" id="inputPassword" class="form-control" required>

    <input type="hidden" name="_csrf_token"
           value="{{ csrf_token('authenticate') }}"
    >

    {#
        Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
        See https://symfony.com/doc/current/security/remember_me.html

        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" name="_remember_me"> Remember me
            </label>
        </div>
    #}

    <button class="btn btn-lg btn-primary" type="submit">
        Sign in
    </button>
</form>
{% endblock %}



Étape 16 - Lancement du site Web



Pour démarrer le site, définissez d'abord un ensemble de serveurs Web:



composer require symfony/web-server-bundle --dev ^4.4.2


Nous lançons le site:



php bin/console server:run


Étape 17 - Test de l'autorisation pour l'utilisateur



Allez à la page 127.0.0.1 : 8000 / login



Nous voyons ceci: Nous nous



image



connectons en utilisant l'e-mail user@example.com et le mot de passe 123654.



Nous voyons que l'autorisation a réussi:



image



Si vous utilisez des données incorrectes, cela supprimera l'erreur des informations d'identification non valides.



Étape 18 - Tester l'autorisation pour l'administrateur



Allez à la page 127.0.0.1 : 8000 / admin / login



Nous voyons ceci: Nous nous



image



connectons en utilisant l'email admin@example.com et le mot de passe 123654.



Apparemment, tout est réussi:



image



Si nous entrons des données incorrectes ou si nous entrons des données de l'utilisateur sur la page / admin / login - une erreur sera générée que les informations d'identification invalides. Pour la page / login la même chose - entrez les données de l'administrateur - il y aura une erreur.



Conclusion



Merci à tous ceux qui ont lu jusqu'à la fin, ont essayé de rédiger le guide avec le plus de détails possible, afin que tout le monde, si nécessaire, puisse faire quelque chose de similaire.



J'ai décidé d'écrire un tutoriel après que je n'ai pas pu trouver d'instructions détaillées pour cette tâche dans la documentation, les guides ou les discussions en anglais, sans parler du matériel en russe.



All Articles