[Symfony 5] Separate Autorisierung für Administratoren und Benutzer mit zwei verschiedenen Entitäten und Anmeldeformularen

Zweck



Erstellen Sie eine separate Autorisierung in Symfony 5:



  • Administrator - verfügt über die Admin- Entität , Anmelde-URL / admin / login
  • Benutzer - hat eine Benutzerentität , Anmelde- URL / Anmeldung
  • Anmeldedaten sollten sich nicht überschneiden, wir können uns nicht als Benutzer auf der Seite / admin / login anmelden
  • Es müssen zwei verschiedene Entitäten erstellt werden
  • Es müssen zwei verschiedene Login-Controller und zwei verschiedene Sicherheitsfunktionen erstellt werden
  • Möglichkeit, die Weiterleitung nach der Autorisierung getrennt voneinander zu konfigurieren
  • Die Möglichkeit, unterschiedliche Autorisierungsdaten zu verwenden (z. B. möchten wir, dass Benutzer ihre E-Mail-Adresse / ihr Kennwort eingeben, und dass der Administrator zusätzlichen Schutz bietet, indem er eine Uuid hinzufügt


Warum wird dieser Leitfaden benötigt?



Meine Aufgabe bestand darin, das Anmeldeformular mit der Benutzerentität in zwei verschiedene zu teilen - für den Benutzer (Entitätsbenutzer) und für den Administrator (Entitätsadministrator) für die normale Funktionalität des Admin-Panels (in diesem Fall EasyAdmin).



In diesem Tutorial werde ich Schritt für Schritt den gesamten Weg malen, beginnend mit der Installation des Frameworks selbst und endend mit der Erstellung von zwei verschiedenen Formen der Autorisierung.



Technische Eigenschaften



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


Das Tutorial ist Ende Juni 2020 relevant.



Schritt 0 - Symfony 5 installieren



Wir gehen davon aus, dass Sie alle erforderlichen Komponenten, einschließlich Composer, im OpenServer-Stammverzeichnis (... / Domains) installiert haben.



composer create-project symfony/website-skeleton auth_project




Schritt 1 - Konfigurieren Sie die Datenbank



Erstellen Sie eine neue Datenbank, nennen Sie sie auth_project, lassen Sie das Passwort und den Benutzer mysql sein. Jetzt müssen wir die .env-Einstellungen neu definieren.



Es sollte so aussehen:




# 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 ###



Schritt 2 - Erstellen Sie eine Benutzerentität



Erstellen Sie die Benutzerentität und wählen Sie E-Mail als eindeutigen Wert aus



php bin/console make:user


Bild



Schritt 3 - Erstellen Sie eine Administratorentität



Wir wiederholen alles, was im vorherigen Schritt beschrieben wurde, anstelle des Namens der Benutzerentität, die wir Admin festgelegt haben



Schritt 4 - Vorrichtungen vorbereiten



Erstellen wir zwei Testkonten, eines für den Benutzer und eines für den Administrator. Wir werden DoctrineFixturesBundle verwenden.



Zuerst müssen Sie es setzen



composer require --dev orm-fixtures


Nach der Installation wird der Ordner DataFixtures in / src angezeigt, in dem die Datei AppFixtures.php bereits erstellt wurde.



Benennen Sie sie in UserFixtures.php um und fügen Sie die erforderlichen Funktionen hinzu



<?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();
    }
}


Das gleiche muss für den Administrator gemacht werden - create 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();
    }
}


Schritt 5 - Migrationen und Fixtures in die Datenbank hochladen



Die Entitäten wurden erstellt, wir haben Fixtures registriert. Es bleibt nun, alles in die Datenbank zu füllen. Die folgenden Aktionen führe ich bei jeder Änderung von Entities oder Fixtures aus




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 #     


Schritt 6 - Autorisierung erstellen



In die Konsole schreiben wir



php bin/console make:auth


Wir stellen die Einstellungen und Namen wie folgt ein:




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


Infolgedessen wird security.yaml aktualisiert und 3 Dateien erstellt



Schritt 7 - Bearbeiten Sie security.yaml



Nachdem die Autorisierung erstellt wurde, sieht security.yaml folgendermaßen aus:




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 }



Wir müssen einen neuen Anbieter admin_user_provider hinzufügen und die Einstellungen für Firewalls ändern .



Schließlich sollte die Datei security.yaml folgendermaßen aussehen:




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 }



Schritt 8 - Benennen Sie die Vorlage login.html.twig um



Dies muss getan werden, da wir die Autorisierung über make: auth neu erstellen werden.

Nennen wir diese Datei.



Schritt 9 - Bearbeiten des UserAuthSecurityController



Die Datei befindet sich im Pfad App \ Controller. Da wir den Namen der Vorlage geändert haben, muss dies im Controller geändert werden.



Was soll der Controller sein:




<?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.');
    }
}


Schritt 10 - Erstellen Sie eine zweite Autorisierung



Wir schreiben in die Konsole:




php bin/console make:auth


Da wir einen neuen app_admin_provider hinzugefügt haben , werden wir aufgefordert , die zu aktualisierende



Bild



Firewall auszuwählen : Bieten Sie nach Auswahl der Firewall an, Entity auszuwählen, und wählen Sie \ App \ Entity \ Admin:



Bild



Schritt 11 - Benennen Sie die soeben erstellte Datei login.html.twig um



Benennen Sie die neu erstellte Datei login.html.twig in admin-login.html.twig um



Schritt 12 - Bearbeiten des soeben erstellten AdminAuthControllers



Ändern Sie den Routen- und Vorlagennamen:




<?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.');
    }
}


Schritt 13 - Bearbeiten Sie die Datei config / route.yaml



Erstellen Sie login_check und admin_login_check, die wir in den Firewall-Einstellungen in der



Datei config / packages / security.yaml definiert haben. So sollte die Datei config / route.yaml aussehen:




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



Schritt 14 - Bearbeiten Sie die Datei templates / secutiry / user-login.html.twig



Fügen Sie dem Tag das Aktionsattribut hinzu:



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



Schritt 15 - Bearbeiten Sie die Datei templates / secutiry / admin-login.html.twig



Fügen Sie dem Tag das Aktionsattribut hinzu:



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



Schritt 16 - Starten Sie die Site



Um die Site zu starten, installieren Sie zuerst das Webserver-Bundle:



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


Wir starten die Seite:



php bin/console server:run


Schritt 17 - Testen der Autorisierung für den Benutzer



Gehen Sie zur Seite 127.0.0.1 : 8000 / login.



Wir sehen dies: Wir



Bild



melden uns mit der E-Mail user@example.com und dem Passwort 123654 an.



Wir sehen, dass die Autorisierung erfolgreich war:



Bild



Wenn Sie falsche Daten verwenden, wird der Fehler Ungültige Anmeldeinformationen ausgeblendet.



Schritt 18 - Testen der Autorisierung für Admin



Gehen Sie zu Seite 127.0.0.1 : 8000 / admin / login



Wir sehen dies: Wir



Bild



melden uns mit der E-Mail-Adresse admin@example.com und dem Passwort 123654 an.



Anscheinend ist alles erfolgreich:



Bild



Wenn wir falsche Daten eingeben oder wenn wir Daten vom Benutzer auf der Seite / admin / eingeben Anmeldung - Bei ungültigen Anmeldeinformationen tritt ein Fehler auf. Für die Seite / login wird dasselbe - geben Sie die Daten von Admin ein - ein Fehler angezeigt.



Fazit



Vielen Dank an alle, die bis zum Ende gelesen haben und versucht haben, die Anleitung so detailliert wie möglich zu malen, damit jeder bei Bedarf etwas Ähnliches tun kann.



Ich beschloss, ein Tutorial zu schreiben, nachdem ich in der englischen Dokumentation, den Anleitungen oder Diskussionen keine detaillierten Anweisungen für diese Aufgabe gefunden hatte, ganz zu schweigen von den Materialien auf Russisch.



All Articles