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
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
Firewall auszuwählen : Bieten Sie nach Auswahl der Firewall an, Entity auszuwählen, und wählen Sie \ App \ Entity \ Admin:
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
melden uns mit der E-Mail user@example.com und dem Passwort 123654 an.
Wir sehen, dass die Autorisierung erfolgreich war:
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
melden uns mit der E-Mail-Adresse admin@example.com und dem Passwort 123654 an.
Anscheinend ist alles erfolgreich:
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.