Как создать пользовательский аутентификатор пароля

Как создать пользовательский аутентификатор пароля

Tip

Посмотрите Система пользовательской аутентификации с Guard (пример API токена), чтобы узнать о более лёгком и гибком способе добиться задач пользовательской аутентификации, вроде этой.

Представьте, что вы хотите позволить доступ к вашей странице только в промежутке с 2 до 4 часов дня по мировому времени. В этой статье, вы узнаете, как сделать это в форме входа (т.е. там, где ваш пользователь отправляет своё имя пользователя и пароль).

Аутентификатор пароля

Для начала, создайте новый класс, реализующий SimpleFormAuthenticatorInterface. В итоге, это позволит вам создавать пользовательскую логику для аутентификации пользователя:

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
// src/Security/TimeAuthenticator.php
namespace App\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface;

class TimeAuthenticator implements SimpleFormAuthenticatorInterface
{
    private $encoder;

    public function __construct(UserPasswordEncoderInterface $encoder)
    {
        $this->encoder = $encoder;
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        try {
            $user = $userProvider->loadUserByUsername($token->getUsername());
        } catch (UsernameNotFoundException $e) {
            // ВНИМАНИЕ: это сообщение будет возвращено клиенту
            // (так что не помещайте тут строки недоверенных/ложных сообщений)
            throw new CustomUserMessageAuthenticationException('Invalid username or password');
        }

        $passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials());

        if ($passwordValid) {
            $currentHour = date('G');
            if ($currentHour < 14 || $currentHour > 16) {
                // ВНИМАНИЕ: это сообщение будет возвращено клиенту
                // (так что не помещайте тут строки недоверенных/ложных сообщений)
                throw new CustomUserMessageAuthenticationException(
                    'Вы можете выполнять вход только с 2 до 4!',
                    array(), // Данные сообщения
                    412 // Условие HTTP 412 неудачно
                );
            }

            return new UsernamePasswordToken(
                $user,
                $user->getPassword(),
                $providerKey,
                $user->getRoles()
            );
        }

        // ВНИМАНИЕ: это сообщение будет возвращено клиенту
        // (так что не помещайте тут строки недоверенных/ложных сообщений)
        throw new CustomUserMessageAuthenticationException('Invalid username or password');
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof UsernamePasswordToken
            && $token->getProviderKey() === $providerKey;
    }

    public function createToken(Request $request, $username, $password, $providerKey)
    {
        return new UsernamePasswordToken($username, $password, $providerKey);
    }
}

Как это работает

Отлично! Теперь вам просто нужно настроить Как создать пользовательский аутентификатор пароля. Но вначале, вы можете узнать больше о том, что делает каждый метод в этом классе.

1) createToken

Когда Symfony начинает обрабатывать запрос, вызывается createToken(), где вы создаёте объект TokenInterface, который содержит нужную вам информацию для аутентификации пользователя (например, имя пользователя и пароль) в authenticateToken() .

Любой объект токена, который вы создадите здесь, будет передан вам позже в authenticateToken().

2) supportsToken

Після того, як Symfony викличе createToken(), вона викличе supportsToken() у вашому класі (і будь-яких інших слухачів аутентифікації), щоб з'ясувати, хто має працювати з токеном. Це просто спосіб дозволити кільком механізмам автентифікації бути використаними для одного брендмауера (отже, ви, наприклад, можете спочатку спробувати автентифікувати користувача через сертифікат або API-ключ, а як резерв - через форму входу).

В основному, вам потрібно просто переконатися, що цей метод повертає "true" для токена, який був створений createToken(). Ваша логіка, швидше за все, має виглядати так само, як цей приклад.

3) authenticateToken

Если supportsToken() возвращает true, Symfony вызовет authenticateToken(). Ваша работа здесь заключается в проверке того, разрешено ли токену выполнять вход; вначале получите объект User через поставщика пользователя, а потом, проверьте парль и текущее время.

Note

"Процесс" того, как вы получите объект User и определите, валиден ли токен (например, проверка пароля), может отличаться в зависимости от ваших требований.

В конечном счёте, ваша задача - вернуть новый объект токена, который будет "аутентифицирован" (т.е. будет иметь как минимум 1 установленную роль), и который имеет внутри объект User.

Внутри этого метода, требуется кодировщик пароля, чтобы проверить его валидность:

1
$passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials());

Это сервис, который уже доступен в Symfony и который использует алгоритм, сконфигурированный в конфигурации безопасности (например, security.yaml) под ключом encoders. Ниже, вы увидите, как внедрить это в TimeAuthenticator.

Конфигурация

Теперь, убедитесь в том, что ваш TimeAuthenticator зарегистрирован, как сервис. Если вы используете конфигурацию services.yaml по умолчанию , то это происходит автоматически.

Наконец, активируйте сервис в разделе firewalls конфигурации безопасности, используя ключ simple_form:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/security.yaml
security:
    # ...

    firewalls:
        secured_area:
            pattern: ^/admin
            # ...
            simple_form:
                authenticator: App\Security\TimeAuthenticator
                check_path:    login_check
                login_path:    login

Ключ simple_form имеет те же опции, что и обычная опция form_login, но с дополнительным ключом authenticator, указывающим на новый сервис. Для деталей, смотрите .

Если создание формы входа в общем для вас в новинку, или если вы не понимаете опции check_path или login_path, смотрите Як налаштувати відповіді аутентифікатора форми входу.