Постачальники користувачів

Дата оновлення перекладу 2024-06-03

Постачальники користувачів

Постачальники користувачів (повторно) завантажують користувачів зі сховища (наприклад, бази даних), засновуючись на "ідентифікаторі користвача" (наприклад, адресі електронної пошти або імені користувача). Див. , щоб дізнатися більше про те, коли використовується постачальник користувачів.

Symfony надає декілька постачальників користувачів:

Постачальник користувачів сутності
Завантажує користувачів з бази даних, використовуючи Doctrine;
Постачальник користувачів LDAP
Завантажує постачальників з сервера LDAP;
Постачальник користувачів памʼяті
Завантажує користувачів з файлу конфігурації;
Ланцюговий постачальник користувачів
Зливае два або більше постачальників користувачів у нового постачальника користувачів.

Постачальник користувачів сутності

Цей найрозповсюдженіший постачальник користувачів. Користувачі зберігаються у базі даних, а постачальник використовує Doctrine, щоб вилучити їх:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# config/packages/security.yaml
security:
    providers:
        users:
            entity:
                # клас сутності, який представляє користувачів
                class: 'App\Entity\User'
                # властивість для запиту - наприклад, імʼя користувача, електронна пошта і т.д.
                property: 'email'

                # необовʼязково: якщо ви використовуєте декілька менеджерів сутностей Doctrine,
                # ця опція визначає, якого використовувати
                #manager_name: 'customer'

    # ...

Використання користувацького запиту для завантаження користувача

Постачальник сутностей може робити запит лише з одного конкретного поля, вказаного ключем конфігурації property. Якщо ви хочете мати трохи більше контролю - наприклад, ви хочете знайти користувача за email або username, ви можете зробити це, реалізувавши UserLoaderInterface у вашому сховищі Doctrine (наприклад, UserRepository):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// src/Repository/UserRepository.php
namespace App\Repository;

use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;

class UserRepository extends ServiceEntityRepository implements UserLoaderInterface
{
    // ...

    public function loadUserByIdentifier(string $usernameOrEmail): ?User
    {
        $entityManager = $this->getEntityManager();

        return $entityManager->createQuery(
                'SELECT u
                FROM App\Entity\User u
                WHERE u.username = :query
                OR u.email = :query'
            )
            ->setParameter('query', $usernameOrEmail)
            ->getOneOrNullResult();
    }
}

Щоб закінчити, видаліть ключ property з постачальника користувачів у security.yaml:

1
2
3
4
5
6
7
8
# config/packages/security.yaml
security:
    providers:
        users:
            entity:
                class: App\Entity\User

    # ...

Тепер, кожний раз, коли Symfony використовуватиме постачальника користувачів, у вашому UserRepository буде викликано метод loadUserByIdentifier().

Постачальник користувачів памʼяті

Не рекомендовано використовувати цього постачальника у реальних додатках через його обмеження і те, наскільки складно управляти користувачами. Він може бути корисним у прототипах додатків та для обмежених додатків, які не зберігають користувачів у базах даних.

Цей постачальник користувачів зберігає всю інформацію користувачів у файлі конфігурації, включно з паролями. Переконайтеся в тому, що пароли правильно хешовані. Див. Хешування та верифікація паролів, щоб дізнатися більше.

Після налаштування хешування, ви можете сконфігурувати всю користувацьку інформацію в security.yaml:

1
2
3
4
5
6
7
8
9
10
# config/packages/security.yaml
security:
    providers:
        backend_users:
            memory:
                users:
                    john_admin: { password: '$2y$13$jxGxc ... IuqDju', roles: ['ROLE_ADMIN'] }
                    jane_admin: { password: '$2y$13$PFi1I ... rGwXCZ', roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] }

    # ...

Caution

При використанні постачальника memory, а не алгоритму auto, вам потрібно обирати алгоритм хешування без солі (тобто, bcrypt).

Ланцюговий постачальник користувачів

Цей постачальник користувачів обʼєднує два або більше інших типів постачальників користувачів (entity і ldap), щоб створити нового постачальника користувачів. Порядок, в якому сконфігуровані постачальники, важливий, так як Symfony шукатиме користувачів з першого постачальника і продовжуватиме шукати їх в інших постачальниках, поки не знайде:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config/packages/security.yaml
security:
    # ...
    providers:
        backend_users:
            ldap:
                # ...

        legacy_users:
            entity:
                # ...

        users:
            entity:
                # ...

        all_users:
            chain:
                providers: ['legacy_users', 'users', 'backend_users']

Створення користувацького постачальника користувачів

Більшість додатків не вимагають створення користувацького постачальника. Якщо ви зберігаєте користувачів у базі даних, на LDAP-сервері або у файлі конфігурації, Symfony це підтримує. Однак, якщо ви завантажуєте користувачів з користувацького місця (наприклад, через API або успадковані зʼєднання бази даних), вам знадобиться створити корситувацького постачальника користувачів.

Спочатку, переконайтеся, що ви дотримувалися Посібника з Безпеки при створенні вашого класу User.

Якщо ви використали команду make:user для створення вашого класу User(і ви відповіли на запитання, що вказують, що вам необхідний користувацький постачальник), то ця команда згенерує гарний скелет для того, щоб почати:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// src/Security/UserProvider.php
namespace App\Security;

use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class UserProvider implements UserProviderInterface, PasswordUpgraderInterface
{
    /**
     * Метод loadUserByIdentifier() було представлено в Symfony 5.3.
     * У попередніх версіях він називався loadUserByUsername()
     *
     * Symfony викликає цей метод, якщо ви використовуєте функції на кшталт switch_user
     * або remember_me. Якщо ви не використовуєте ці функції, вам не потрібно реалізовувати
     * цей метод.
     *
     * @throws UserNotFoundException, якщо користувача не знайдено
     */
    public function loadUserByIdentifier(string $identifier): UserInterface
    {
        // Завантажити обʼєкт User з вашого джерела даних або викликати UserNotFoundException.
        // Аргумент $identifier - те значення, яке повертається методом
        // getUserIdentifier() у вашому класі User.
        throw new \Exception('TODO: fill in loadUserByIdentifier() inside '.__FILE__);
    }

    /**
     * Оновлює користувача після повторного завантаження з сесії.
     *
     * Коли користувач увійшов у систему, на початку кожного запиту, обʼєкт
     * User завантажується з сесії, а потім викликається цей метод. Ваша задача
     * - переконатисяс, що дані користувача все ще свіжі, шляхом, наприклад, повторного
     * запиту свіжих даних користувача.
     *
     * Якщо ваш брандмауер "stateless: true" (для чистого API), цей метод
     * не викликається.
     *
     * @return UserInterface
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
        }

        // Повернути обʼєкт User післе того, як переконалися. що його дані "свіжі".
        // Або викликати UserNotFoundException, якщо користувач вже не існує.
        throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
    }

    /**
     * Вказує Symfony використати цього постачальника для цього класу User.
     */
    public function supportsClass(string $class)
    {
        return User::class === $class || is_subclass_of($class, User::class);
    }

    /**
     * Оновлює зашифрований пароль користувача, зазвичай для використання кращого алгоритму хешування.
     */
    public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
    {
        // ЗРОБИТИ: коли використовуються хешовані паролі, цей метод повинен:
        // 1. зберігати новий пароль у сховище користувача
        // 2. оновлювати обʼєкт $user з $user->setPassword($newHashedPassword);
    }
}

Більшість роботи вже зроблена! Прочитайте коментарі у коді та оновіть розділи ЗРОБИТИ, щоб закінчити з постачальником користувачів. Коли ви завершите, повідомте Symfony про постачальника користувачів, додавши його у security.yaml:

1
2
3
4
5
6
# config/packages/security.yaml
security:
    providers:
        # the name of your user provider can be anything
        your_custom_user_provider:
            id: App\Security\UserProvider

Нарешті, оновіть файл config/packages/security.yaml, щоб встановити ключ provider як your_custom_user_provider у всіх брандмауерах, які будуть використовувати цього користувацького постачальника.