Symfony 身份验证:为什么用户密码未在 TokenInterface 中序列化

Symfony authentication : Why user password is not serialized in TokenInterface

我配置了防火墙来保护我的部分应用程序 (/manager)。我还设置了一个登录表单来进行身份验证,但我在登录表单上循环。

我确定密码是正确的,经过一番调查后我发现身份验证工作正常(我已通过身份验证)但紧接着,当 symfony 刷新用户时,它会注销我,因为用户是不一样。 我发现这是由密码不匹配引起的。问题是,当我通过身份验证器登录时,用户被序列化(我猜是使用 TokenInterface),但字段 'password' 在 TokenInterface 中(不在数据库中)设置为 null。 我在我的应用程序中使用 JMSSerialiserBundle,我不知道问题是否出在它身上。 你能帮帮我吗?

我的用户class

/**
 * @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

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() 它也不起作用。我不知道我是否应该覆盖某些功能或实现接口。

检查 Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasUserChanged()

我的建议是在您的用户 class 上创建名为 isEqualTo 的新方法,并且您的 class 应该实现 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;
}