Аутентификация Symfony: почему пароль пользователя не сериализуется в TokenInterface

Я настроил брандмауэр для защиты части моего приложения (/manager). Я также настраиваю форму входа для аутентификации, но я зацикливаюсь на форме входа.

Я уверен, что пароль правильный, и после некоторого расследования я обнаружил, что аутентификация работает хорошо (я был аутентифицирован), но сразу после этого, когда symfony обновляет пользователя, он выходит из системы, потому что пользователь не тот. Я обнаружил, что это было вызвано несоответствием пароля. Проблема в том, что когда я вхожу в систему с помощью аутентификатора, пользователь сериализуется (я думаю, с TokenInterface), но поле «пароль» имеет значение null в TokenInterface (не в базе данных). Я использую JMSSerialiserBundle в своем приложении, я не знаю, возникает ли проблема. Можете вы помочь мне ?

Мой пользовательский класс

/**
 * @ORM\Entity(repositoryClass = "App\Repository\UtilisateurRepository")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name = "type", type = "string")
 * @ORM\DiscriminatorMap({"eleve" = "Eleve", "gestionnaire" = "Gestionnaire"})
 */
abstract class Utilisateur implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type = "integer")
     * @Serializer\Groups({"id"})
     */
    private $id;

    /**
     * @ORM\Column(type = "string", length=255)
     * @Serializer\Groups({"Utilisateur"})
     */
    private $nom;

    /**
     * @ORM\Column(type = "string", length=255)
     * @Serializer\Groups({"Utilisateur"})
     */
    private $prenom;

    /**
     * @ORM\Column(type = "string", length=255)
     * @Serializer\Groups({"Utilisateur"})
     */
    private $username;

    /**
     * @ORM\Column(type = "string", length=255)
     * @Serializer\Exclude()
     */
    private $password;

    /**
     * @ORM\Column(type = "string", length=1024, nullable=true)
     * @Serializer\Groups({"Login"})
     */
    private $token;

Мой класс аутентификатора

class ManagerAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(
        EntityManagerInterface $entityManager,
        UrlGeneratorInterface $urlGenerator,
        CsrfTokenManagerInterface $csrfTokenManager,
        UserPasswordEncoderInterface  $passwordEncoder
    )
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'username' => $request->request->get('username'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];

        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['username']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $userProvider->loadUserByUsername($credentials['username']);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Username could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password'], $user->getSalt());
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        //On redirige sur la page précédente sauf si on vient du login
        $targetPath = $this->getTargetPath($request->getSession(), $providerKey);
        $loginRoute = $this->urlGenerator->generate('app_login');

        if ($targetPath && strpos($loginRoute, $targetPath) === false) {
            return new RedirectResponse($targetPath);
        }

        //Rediriger vers l'index
        return new RedirectResponse($this->urlGenerator->generate('accueil_index'));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}

Даже без @Serializer\Exclude() это не работает. Я не знаю, должен ли я переопределить какую-то функцию или реализовать интерфейс.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
0
122
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проверьте Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasUserChanged()

Мой совет: создайте новый метод в вашем пользовательском классе с именем isEqualTo, и ваш класс должен реализовать EquatableInterface.

/**
 * @param UserInterface $user
 * @return bool
 */
public function isEqualTo(UserInterface $user): bool
{
    return $this->username === $user->getUsername() && $this->id === $user->getId();
}

Также вам может понадобиться добавить эти два метода.

/**
 * @see \Serializable::serialize()
 */
public function serialize(): string
{
    return serialize([$this->id, $this->username,]);
}

/**
 * @see \Serializable::unserialize()
 * @param string $serialized
 * @return User
 */
public function unserialize($serialized): self
{
    [$this->id, $this->username] = unserialize($serialized);

    return $this;
}

Используя ваш метод, он работает, но я хотел бы следовать хорошей практике и проверять пароль. Я нашел ссылку, которая может мне помочь. Вечером протестирую и отпишусь. Ссылка : christophe-meneses.fr/article/…

Tristan Le Gacque 26.07.2019 16:51

Не работает. На данный момент я использовал ваше решение, но если я выясню это, я обновлю ветку решением:/

Tristan Le Gacque 30.07.2019 09:55

Другие вопросы по теме