[Symfony 5] Otorisasi terpisah untuk admin dan pengguna dengan dua entitas berbeda dan formulir login

tujuan



Buat otorisasi terpisah di Symfony 5:



  • Administrator - akan memiliki entitas Admin , url login / admin / login
  • Pengguna - akan memiliki entitas Pengguna , url login / login
  • Data login tidak boleh tumpang tindih, kita tidak bisa login sebagai Pengguna di halaman / admin / login
  • Dua Entitas berbeda harus dibuat.
  • Dua pengontrol yang berbeda harus dibuat untuk login dan dua pengamanan yang berbeda
  • Kemungkinan pengaturan penerusan setelah otorisasi terpisah satu sama lain
  • Kemampuan untuk menggunakan data otorisasi yang berbeda (misalnya, untuk Pengguna kami ingin pengguna memasukkan email / kata sandi, dan untuk Admin untuk memberikan perlindungan tambahan dengan menambahkan semacam Uuid


Mengapa panduan ini diperlukan?



Tugas saya adalah untuk membagi formulir login dengan entitas Pengguna menjadi dua yang berbeda - untuk pengguna (entitas Pengguna) dan untuk administrator (entitas Admin) untuk fungsi normal panel admin (dalam hal ini, EasyAdmin).



Dalam tutorial ini, saya akan menjelaskan keseluruhan langkah demi langkah, mulai dengan menginstal framework itu sendiri, dan diakhiri dengan membuat dua bentuk otorisasi yang berbeda.



Spesifikasi



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


Tutorialnya terkini pada akhir Juni 2020.



Langkah 0 - Instal Symfony 5



Kami akan menganggap bahwa Anda telah menginstal semua komponen yang diperlukan, termasuk Komposer, di direktori root OpenServer (... / domains).



composer create-project symfony/website-skeleton auth_project




Langkah 1 - mengkonfigurasi basis data



Buat database baru, beri nama auth_project, biarkan kata sandi dan pengguna menjadi mysql. Sekarang kita perlu mengganti pengaturan .env



Seharusnya seperti ini:




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



Langkah 2 - membuat entitas Pengguna



Buat entitas Pengguna, pilih email sebagai nilai unik



php bin/console make:user


gambar



Langkah 3 - Buat Entitas Admin



Kami ulangi semua yang dijelaskan pada langkah sebelumnya, alih-alih nama entitas Pengguna yang kami atur Admin



Langkah 4 - Siapkan Perlengkapan



Kami akan membuat 2 akun uji, satu untuk Pengguna, yang kedua untuk Admin. Kami akan menggunakan DoctrineFixturesBundle



Pertama, Anda perlu meletakkannya



composer require --dev orm-fixtures


Setelah instalasi, folder DataFixtures akan muncul di / src, di mana file AppFixtures.php sudah akan dibuat.



Ubah nama menjadi UserFixtures.php dan tambahkan fungsionalitas yang diperlukan untuk itu



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


Hal yang sama perlu dilakukan untuk admin - buat 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();
    }
}


Langkah 5 - unggah migrasi dan jadwal ke basis data



Entitas telah dibuat, kami telah mendaftarkan fixture, tetap sekarang mengisi semua ke dalam database, saya melakukan tindakan berikut dengan setiap perubahan entitas atau fixture




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 #     


Langkah 6 - buat otorisasi



Di konsol kami meresepkan



php bin/console make:auth


Kami menetapkan pengaturan dan nama sebagai berikut:




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


Akibatnya, security.yaml akan diperbarui dan 3 file akan dibuat



Langkah 7 - edit security.yaml



Setelah otorisasi dibuat, security.yaml terlihat seperti ini:




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 }



Kami perlu menambahkan admin_user_provider penyedia baru dan mengubah pengaturan untuk firewall .



Akhirnya , file security.yaml akan terlihat seperti ini:




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 }



Langkah 8 - ganti nama template login.html.twig



Ini perlu dilakukan, karena kami akan membuat kembali otorisasi melalui make: auth.

Beri nama file ini.



Langkah 9 - edit UserAuthSecurityController



File ini terletak di path App \ Controller, karena kami mengubah nama templat, ini perlu diubah di controller.



Apa yang seharusnya menjadi pengontrol:




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


Langkah 10 - membuat otorisasi kedua



Kami menulis di konsol:




php bin/console make:auth


Karena kami telah menambahkan app_admin_provider baru , kami akan diminta untuk memilih firewall mana yang ingin kami perbarui:



gambar



Setelah memilih firewall, tawarkan untuk memilih Entity, pilih \ App \ Entity \ Admin:



gambar



Langkah 11 - ganti nama login.html.twig yang baru dibuat



Ganti nama login.html.twig yang baru dibuat menjadi admin-login.html.twig



Langkah 12 - mengedit AdminAuthController yang baru saja kita buat



Ubah rute dan nama templat:




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


Langkah 13 - mengedit file config / routes.yaml



Buat login_check dan admin_login_check, yang kami tentukan dalam pengaturan firewall di file config / paket / security.yaml



Bagaimana seharusnya file config / route.yaml terlihat seperti:




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



Langkah 14 - edit file templat / secutiry / user-login.html.twig



Tambahkan atribut tindakan ke tag:



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



Langkah 15 - edit file template / secutiry / admin-login.html.twig



Tambahkan atribut tindakan ke tag:



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



Langkah 16 - meluncurkan situs web



Untuk memulai situs, atur dulu bundel server-web:



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


Kami meluncurkan situs:



php bin/console server:run


Langkah 17 - menguji otorisasi untuk Pengguna



Pergi ke halaman 127.0.0.1 : 8000 / login



Kami melihat ini: Kami



gambar



masuk menggunakan email user@example.com dan kata sandi 123654.



Kami melihat bahwa otorisasi berhasil:



gambar



Jika Anda menggunakan data yang salah, itu akan menghapus kesalahan kredensial tidak valid.



Langkah 18 - menguji otorisasi untuk Admin



Pergi ke halaman 127.0.0.1 : 8000 / admin / login.



Kita melihat ini: Kami



gambar



login menggunakan email admin@example.com dan kata sandi 123654.



Tampaknya, semuanya berhasil:



gambar



Jika kita memasukkan data yang salah atau jika kita memasukkan data dari Pengguna pada halaman / admin / login - kesalahan akan dilemparkan bahwa kredensial tidak valid. Untuk halaman / login hal yang sama - masukkan data dari Admin - akan ada kesalahan.



Kesimpulan



Terima kasih kepada semua orang yang membaca sampai akhir, mencoba menulis panduan ini sedetail mungkin, sehingga setiap orang, jika perlu, dapat melakukan hal serupa.



Saya memutuskan untuk menulis tutorial setelah saya tidak menemukan instruksi terperinci untuk tugas ini dalam dokumentasi, panduan, atau diskusi dalam Bahasa Inggris, belum lagi materi dalam bahasa Rusia.



All Articles