Как создать пользовательский аутентификатор пароля¶
Tip
Посмотрите How to Create a Custom Authentication System with Guard, чтобы узнать о более лёгком и гибком способе добиться задач пользовательской аутентификации, вроде этой.
Представьте, что вы хотите позволить доступ к вашей странице только в промежутке с 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);
}
}
|
Как это работает¶
Отлично! Теперь вам просто нужно настроить Configuration. Но вначале, вы можете узнать больше о том, что делает каждый метод в этом классе.
1) createToken¶
Когда Symfony начинает обрабатывать запрос, вызывается createToken()
, где вы создаёте объект
TokenInterface
,
который содержит нужную вам информацию для аутентификации пользователя (например,
имя пользователя и пароль) в authenticateToken()
.
Любой объект токена, который вы создадите здесь, будет передан вам позже в authenticateToken()
.
2) supportsToken¶
After Symfony calls createToken()
, it will then call supportsToken()
on your class (and any other authentication listeners) to figure out who should
handle the token. This is just a way to allow several authentication mechanisms
to be used for the same firewall (that way, you can for instance first try
to authenticate the user via a certificate or an API key and fall back to
a form login).
Mostly, you just need to make sure that this method returns true
for a
token that has been created by createToken()
. Your logic should probably
look exactly like this example.
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
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
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<!-- config/packages/security.xml --> <?xml version="1.0" encoding="UTF-8"?> <srv:container xmlns="http://symfony.com/schema/dic/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <config> <!-- ... --> <firewall name="secured_area" pattern="^/admin" > <simple-form authenticator="App\Security\TimeAuthenticator" check-path="login_check" login-path="login" /> </firewall> </config> </srv:container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// config/packages/security.php // ... use App\Security\TimeAuthenticator; $container->loadFromExtension('security', array( 'firewalls' => array( 'secured_area' => array( 'pattern' => '^/admin', 'simple_form' => array( 'provider' => ..., 'authenticator' => App\Security\TimeAuthenticator::class, 'check_path' => 'login_check', 'login_path' => 'login', ), ), ), ));
Ключ simple_form
имеет те же опции, что и обычная опция form_login
,
но с дополнительным ключом authenticator
, указывающим на новый сервис.
Для деталей, смотрите Form Login Configuration.
Если создание формы входа в общем для вас в новинку, или если вы не понимаете
опции check_path
или login_path
, смотрите How to Customize Redirect After Form Login.
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.