我为自定义用户对象构建了一个UserType。其中一个字段是密码。
理想情况下,当我将表单作为数据转换器/相关提交而不必在控制器中处理时,我希望它对密码进行编码。但是,它是一个盐渍密码,所以这会产生一个问题,因为我喜欢每次生成密码时重新生成盐。我不知道如何在DataTransformer中获得额外的价值。
所以,我基本上有两个问题:
感谢。
答案 0 :(得分:5)
所以,我做了一些挖掘和寻找,并从FOSUserBundle中获取了一些线索。
要回答我的第一个问题,看起来两者都不是最好的选择,因此第二个问题没有实际意义。
我最终做了什么:
$plainPassword
添加新字段。然后我将UserType的映射更改为此字段,而不是直接更改$ password。
$plainPassword
User::eraseCredentials
updateUser()
,它处理密码的实际编码(不再处理Controller,是的!)。这或多或少是FOSUserBundle所做的。然后他们手动调用updateUser(我相信来自Controller)。但是,我希望能够在大多数情况下忘记这一步骤。这就是Doctrine Events的用武之地。
我添加了两个Doctrine事件监听器(均为UserManager):prePersist和preUpdate。
两者基本上都检查我们是否正在处理User对象,然后调用updateUser()
来更新用户密码。 preUpdate必须有一个额外的步骤来手动设置新值。
为确保触发,User::setPlainPassword()
清除密码并刷新我的盐(这是必要的,因为plainPassword不是映射属性,因此只需更改它就不允许用户触发preUpdate)。
很多移动的部分,但现在我已经准备好了,每当我使用User::setPlainPassword()
更改明文密码(无论在哪里)时,它现在都会正确编码并保存实际的密码值。活泉!
namespace My\Bundle\UserBundle\DependencyInjection;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\LifecycleEventArgs;
use My\Bundle\UserBundle\Entity\User;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
class UserManager
{
protected $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
public function getEncoder(User $user)
{
return $this->encoderFactory->getEncoder($user);
}
public function updateUser(User $user)
{
$plainPassword = $user->getPlainPassword();
if (!empty($plainPassword)) {
$encoder = $this->getEncoder($user);
$user->setPassword($encoder->encodePassword($plainPassword, $user->getSalt()));
$user->eraseCredentials();
}
}
public function preUpdate(PreUpdateEventArgs $event)
{
$user = $event->getEntity();
if (!($user instanceof \GamePlan\Bundle\UserBundle\Entity\User)) {
return;
}
$this->updateUser($user);
$event->setNewValue('password', $user->getPassword());
//die($event->getOldValue('password') . ' ' . $event->getNewValue('password') . ' ' . $event->hasChangedField('password') ? 'Y' : 'N');
}
public function prePersist(LifecycleEventArgs $event)
{
$user = $event->getEntity();
if (!($user instanceof \GamePlan\Bundle\UserBundle\Entity\User)) {
return;
}
$this->updateUser($user);
}
}
// In My\Bundle\UserBundle\Entity\User
public function setPlainPassword($plainPassword)
{
$this->plainPassword = $plainPassword;
// Change some mapped values so preUpdate will get called.
$this->refreshSalt(); // generates a new salt and sets it
$this->password = ''; // just blank it out
}
# In My/Bundle/UserBundle/Resources/config/services.yml
services:
my_user.manager:
class: My\Bundle\UserBundle\DependencyInjection\UserManager
arguments:
- @security.encoder_factory
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }