Подтвердить что ты не робот

Symfony 3, заполняющий токен и обновляющий пользователь

репозиторий с проблемой

У меня есть форма для объекта Пользователь с полем электронной почты:

->add('email', EmailType::class, [
                'constraints' => [
                    new NotBlank(),
                    new Email([
                        'checkMX' => true,
                    ])
                ],
                'required' => true
            ])

когда я редактирую электронную почту на что-то вроде [email protected] и отправляю форму, она показывает мне ошибку "Это значение не является допустимым адресом электронной почты". Это нормально, но после этого symfony заполняет неправильный адрес электронной почты в токен, и когда я перехожу на любую другую страницу или просто перезагружаю страницу, я получаю следующее:

ПРЕДУПРЕЖДЕНИЕ безопасности Имя пользователя не найдено у выбранного пользователя провайдера.

Я думаю, что вопрос: почему symfony заполняет неверный адрес электронной почты, который не прошел проверку в токен и как я мог его предотвратить?

контроллер:

public function meSettingsAction(Request $request)
    {

        $user = $this->getUser();
        $userUnSubscribed = $this->getDoctrine()->getRepository('AppBundle:UserUnsubs')->findOneBy(
            [
                'email' => $user->getEmail(),
            ]
        );

        $form = $this->createForm(UserSettingsType::class, $user);
        $form->get('subscribed')->setData(!(bool)$userUnSubscribed);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /**
             * @var $user User
             */
            $user = $form->getData();

            /** @var UploadedFile $avatar */
            $avatar = $request->files->get('user_settings')['photo'];

            $em = $this->getDoctrine()->getManager();

            if ($avatar) {
                $avatar_content = file_get_contents($avatar->getRealPath());
                $avatarName = uniqid().'.jpg';
                $oldAvatar = $user->getPhoto();
                $user
                    ->setState(User::PHOTO_STATE_UNCHECKED)
                    ->setPhoto($avatarName);
                $gearmanClient = $this->get('gearman.client');
                $gearmanClient->doBackgroundDependsOnEnv(
                    'avatar_content_upload',
                    serialize(['content' => $avatar_content, 'avatarName' => $avatarName, 'oldAvatar' => $oldAvatar])
                );
            }

            $subscribed = $form->get('subscribed')->getData();
            if ((bool)$userUnSubscribed && $subscribed) {
                $em->remove($userUnSubscribed);
            } elseif (!(bool)$userUnSubscribed && !$subscribed) {
                $userUnSubscribed = new UserUnsubs();
                $userUnSubscribed->setEmail($form->get('email')->getData())->setTs(time());
                $em->persist($userUnSubscribed);
            }
            $user->setLastTs(time());
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            $this->get('user.manager')->refresh($user);

            return $this->redirectToRoute('me');
        }

        return $this->render(
            ':user:settings.html.twig',
            [
                'form' => $form->createView(),
            ]
        );
    }

UPD: он отлично работает, если я изменяю OAuthProvider:

/**
 * @param \Symfony\Component\Security\Core\User\UserInterface $user
 *
 * @return \Symfony\Component\Security\Core\User\UserInterface
 */
public function refreshUser(UserInterface $user)
{
    return $this->loadUserByUsername($user->getName());
}

в

/**
 * @param \Symfony\Component\Security\Core\User\UserInterface $user
 *
 * @return \Symfony\Component\Security\Core\User\UserInterface
 */
public function refreshUser(UserInterface $user)
{
    return $this->userManager($user->getId());
}

но, похоже, это грязный хак.

Спасибо.

4b9b3361

Ответ 1

Это сложно, благодаря репозиторию было легче изолировать проблему. Вы привязываете объект пользователя к токену аутентификации к методу createForm(). После

$form->handleRequest($request)

вызов электронной почты с объекта пользователя токена обновляется.

Сначала я решил решить эту проблему, реализовав EquatableInterface.html в объекте User, но это не сработало, поскольку сравниваемые у объекта уже был неправильный набор адресов электронной почты.

Также может быть полезно реализовать интерфейс EquatableInterface, который определяет метод для проверки того, равен ли пользователь текущему пользователю. Для этого интерфейса требуется метод isEqualTo().)

Чем я думал о том, чтобы заставить перезагрузить пользователя из db и сбросить токен безопасности, но в этом мне показалось, что достаточно просто обновить текущий пользовательский объект из базы данных в случае, если форма терпит неудачу:

$this->get('doctrine')->getManager()->refresh($this->getUser());`

В вашем контроллере это решит вашу проблему.

/**
 * @Route("/edit_me", name="edit")
 * @Security("has_role('ROLE_USER')")
 */
public function editMyselfAction(Request $request) {
    $form = $this->createForm(User::class, $this->getUser());

    if ($request->isMethod(Request::METHOD_POST)) {
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();
        } else {
            $this->get('doctrine')->getManager()->refresh($this->getUser());
        }
    }

    return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}

Альтернативное решение

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

Ответ 2

Ваш токен пользователя, по-видимому, обновляется формой, даже если ограничение электронной почты останавливает флеш.

Вы можете проверить, прошла ли ваша форма после функции isValid? Возможно, вы можете попытаться избежать этого с помощью прослушивателя событий или валидатора.

С событием SUBMIT вы должны проверить целостность электронной почты, а затем добавить FormError, чтобы избежать refreshUser.