В какой части кода Symfony Security и Lexik JWT Bundle происходит аутентификация по логину и паролю?

Привет! У меня есть задача - написать свой сервис, который будет проверять логин и пароль пользователя, аналогично api/login_check при использовании Lexik JWT Bundle, и возвращать стандартный JWT токен.

Моя основная цель - понять механизм работы безопасности (security) и Lexik JWT, а в долгосрочной перспективе - реализовать проверку логина и пароля своим собственным классом и интегрировать его в security.

Однако, я не могу понять, кто конкретно проверяет логин и пароль в Lexik JWT Bundle. На данный момент я выследил маршрут до Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator, метод authenticate, возвращающий Passport (содержащий логин, пароль и другие параметры из входящего JSON), и метод onAuthenticationSuccess, в котором уже генерируется JWT токен.

Исходя из этого, у меня есть следующие вопросы:
1) Кто проводит проверку логина и пароля между методами authenticate и onAuthenticationSuccess в JsonLoginAuthenticator?
2) Как лучше всего поступить, если требуется проводить собственную проверку логина и пароля? Например, при замене пароля на другое поле, например, код SMS, но не в рамках двухфакторной аутентификации и не проверки API-ключа при каждом запросе.

Я использую Symfony 7.
  • 19 января 2024 г. 6:40
Ответы на вопрос 2
Привет! В системе безопасности Symfony аутентификация пользователя обычно происходит в несколько этапов, которые включают взаимодействие между различными компонентами Security Bundle.

1) Проверка логина и пароля обычно происходит через "user provider" и "user checker" со стороны Symfony. После того как `JsonLoginAuthenticator` создает `Passport`, этот `Passport` передается в `UserAuthenticationProvider`, который проверяет подлинность учетных данных пользователя.

На этапе работы с `Passport`, `UserAuthenticationProvider` использует `UserProviderInterface` для загрузки пользователя из вашего источника пользователей (например, базы данных через Doctrine) и `UserCheckerInterface` для проверки, что пользователь не заблокирован и активен. Если используется `PasswordCredentials`, то при этом еще происходит проверка введенного пароля с помощью `PasswordHasher`.

2) Если вам требуется проводить собственную проверку логина и пароля (или других учетных данных), то вы можете создать собственный Authenticator, расширив класс `AbstractLoginFormAuthenticator` или реализуя интерфейс `AuthenticatorInterface`.

В вашем собственном Authenticator вы можете определить логику в методе `authenticate`, заполняя `Passport` своими учетными данными, которые вы хотите проверить. Например, вы можете использовать `CustomCredentials` вместо `PasswordCredentials` и предоставить функцию обратного вызова для проверки этих учетных данных.

Что касается LexikJWTBundle, после успешной аутентификации (после `onAuthenticationSuccess`), контроль переходит к `JWTTokenAuthenticator`, который ничего не знает о логинах и паролях; его задача — просто взять данные аутентифицированного пользователя и сгенерировать JWT токен. Таким образом, ваш собственный Authenticator должен интегрироваться на уровне Security Bundle и вписываться в существующий процесс создания токенов LexikJWTBundle.

Для того чтобы использовать собственный Authenticator, вам нужно будет сконфигурировать его в `security.yaml` и определить его как сервис в контейнере приложения. Это позволит системе безопасности Symfony использовать ваш Authenticator для аутентификации пользователей.

Вот пример кастомного Authenticator:

```php
// src/Security/CustomAuthenticator.php
namespace App\Security;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;

class CustomAuthenticator extends AbstractAuthenticator
{
    public function supports(Request $request): ?bool
    {
        // Ваша логика для определения, следует ли использовать этот Authenticator
    }

    public function authenticate(Request $request): Passport
    {
        // Ваша логика для создания Passport
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        // Ваша логика для ответа после успешной аутентификации
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        // Ваша логика для ответа в случае ошибки при аутентификации
    }
}
```

Не забудьте зарегистрировать ваш `CustomAuthenticator` в `security.yaml`.

Надеюсь, это дает вам представление о том, как работает система аутентификации в Symfony и как вы можете реализовать свою собственную логику для проверки учетных данных.
Зачем вам разбираться, как работает сложный бандл. Вам нужно разобраться как это сделать минимальными усилиями. Могу лишь подсказать как это сделать. Потому что описывать как работает бандл долго и ни к чему. 

1. Делаете новый роут и в нем проверяете входящие данные. Если это логин и код из смс, то ищете эту связку в базе и если нашлась, то формируете JWT и отдаете frontend приложению.
2. Делаете Аутентификатор, который берет заголовок токена из запроса, извлекает из него токен и проверяет его на различную валидацию: подпись, срок действия и т д

Ну а дальше если есть вопросы можете написать их в комментариях.
Похожие вопросы