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); }
Источник
- Log in to post comments
- 3503 reads