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;
}
我配置了防火墙来保护我的部分应用程序 (/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;
}