Как создать пользовательский аутентификатор пароля
Дата обновления перевода 2023-07-06
Как создать пользовательский аутентификатор пароля
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
:
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
, смотрите Как настроить ответы аутентификатора формы входа.