symfony fosuserbundle - Как переопределить логику логина?
Primary tabs
Не могу понять, где можно изменить логику идентификации пользователя.
Дано:
Использую FOSUserBundle Symfony.
Были переопределена форма логина. По умолчанию, идентификация происходит по username и password (либо по email - можно переопределить). Мне же нужно, чтобы пользователь идентифицировался по полю mobileNumber.
{{ form_start(form, {'method': 'post', 'action': path('fos_user_security_check')}) }}
{% if csrf_token %}
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
{% endif %}
<div class="main-text">Войти</div>
<div class="text-description-mobile-phone">Мобильный телефон</div>
{{ form_widget(form.mobileNumber) }}
<div class="text-description-password">Код из СМС</div>
{{ form_widget(form.code) }}
<div class="line"></div>
<input type="submit" id="_submit" name="_submit" value="{{ 'Войти'|trans }}" class="enter-button"/>
{{ form_end(form) }}Что уже достигнуто:
Можно было бы переопределить SecurityController (т.е. скопировать его код к себе в приложение, чтобы оно использовало мой класс, а не из вендора), НО нет смысла, т.к. он не принимает POST и данные формы вообще не обрабатывает.
Не понятно, где конкретно происходит проверка. Вроде бы в самом бандле она реализуется классом LoginManager, но он не переопределяется(или я не знаю, как). Он как-то вызывается внутренней логикой бандла.
При submit-е формы POST отправляется по адресу login_check, но где он находится в бандле не очень понятно, и где его можно переопределить у меня тоже. И как - тоже. Есть предположение, что этот путь можно дать методу CheckAction SecurityController-а, но в бандле этот метод не определён, а аутентификация работает. Также путь 'fos_user_security_check' прописан в action-e формы логина. Это зацепка, но она тонет в путанице роутингов и темноте инкапсуляции.
Решение:
Решение нашлось внезапно на просторах интернета.
- Помещаем всю внутреннюю логику залогинивания в loginAction()
- Изменяем action обрабатываемой формы на login или
{{ form_start(form, {'method': 'post', 'action': path('fos_user_security_login')}) }}
Вот код нового контроллера:
/**
* @param Request $request
*
* @return Response
*
* @Route("login")
*/
public function loginAction(Request $request): Response
{
// Объявляем данные, которые передаются в форму
$data = [];
$data['error'] = null;
// Это данные, полученные из формы
$formData = $request->get('form');
// Но для отладки можно просто присвоить данные напрямую
// $mobileNumber = '+71112223456';
// Проверка "если был submit формы"
if(!empty($request->get('submit'))) {
// Достаём данные из формы
$mobileNumber = $formData['mobileNumber'];
$code = $formData['code'];
// Получаем security encoder Symfony
$factory = $this->get('security.encoder_factory');
// Загрузка пользователя из базы
// Если вы используете FOSUserBundle и принимаете username можно как-то так:
// $user_manager = $this->get('fos_user.user_manager');
// $user = $user_manager->findUserByUsername(['_username' => $username]);
// Но мы я принимаю mobileNumber, поэтому воспользуюсь Doctrine:
$user = $this->getDoctrine()->getManager()->getRepository(User::class)
->findOneBy(['mobileNumber' => $mobileNumber]);
/// Закончили загрузку
// Проверяем, существует ли у нас такой пользователь
if(!$user){
// Этот вариант рабочий, отправляет пользователя на сраницу регистрации, если введённого номера нет в базе.
$url = $this->generateUrl('fos_user_registration_register');
$response = new RedirectResponse($url);
return $response;
// Этот вариант используем для отладки
// $data['error'] = 'Неверные данные';
// return $this->renderLogin($data);
}
// Генерируем csrfToken
$csrfToken = $this->has('security.csrf.token_manager')
? $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue()
: null;
$data['csrf_token'] = $csrfToken;
// Закончили генерировать
// Верификация пароля (если таковой имеется). У меня регистрация только по мобильному номеру, поэтому закомментировано
// $encoder = $factory->getEncoder($user);
// $salt = $user->getSalt();
//
// if(!$encoder->isPasswordValid($user->getPassword(), $_password, $salt)) {
// return new Response(
// 'Username or Password not valid.',
// Response::HTTP_UNAUTHORIZED,
// array('Content-type' => 'application/json')
// );
// }
// Закончили верифицировать
// Пароль подтверждён! Теперь запишем выбранного пользователя в сессию
// Процесс залогинивания пользователя
// Третий параметр, "main" может быть другим, в соответствии с вашим firewall-ом в security.yml
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
// Если имя используемого firewall-a не "main", то используйте первую строку:
// $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
$this->get('session')->set('_security_main', serialize($token));
// Вручную вызываем событие login
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
/*
* Теперь пользователь аутентифицирован !!!!
* Добавьте сюда свой код, если вы хотите сделать что-то ещё, например, печать формы или переадресацию
*/
return $this->redirectToRoute("meeting");
}
return $this->renderLogin($data);
}Источник
https://ourcodeworld.com/articles/read/459/how-to-authenticate-login-manually-an-user-in-a-controller-with-or-without-fosuserbundle-on-symfony-3
- Log in to post comments
- 4341 reads