Symfony2表单在提交时编码密码

sam*_*ime 2 php forms symfony

我为自定义User对象构建了一个UserType.其中一个字段是密码.

理想情况下,当我将表单作为数据转换器/相关提交而不必在控制器中处理时,我希望它对密码进行编码.但是,它是一个盐渍密码,所以这会产生一个问题,因为我喜欢每次生成密码时重新生成盐.我不知道如何在DataTransformer中获得额外的价值.

所以,我基本上有两个问题:

  • 将它作为UserType(AbstractType)的一部分进行编码是一个好/坏的主意,还是应该在Controller中处理它?
  • 我如何将所需信息传递给我的DataTransformer以实现这一目标?

谢谢.

sam*_*ime 5

所以,我做了一些挖掘和寻找,并从FOSUserBundle获得了一些线索.

为了回答我的第一个问题,看起来两者都不是最好的选择,因此第二个问题没有实际意义.

我最终做了什么:

  • 向我的用户实体添加一个新字段$plainPassword.然后我将UserType的映射更改为此字段,而不是直接更改$ password.
    • 一定要空出$plainPasswordUser::eraseCredentials
  • 制作了一个名为UserManager的新服务.在这里,我给它一个函数updateUser()来处理密码的实际编码(不再处理Controller,是的!).

这或多或少是FOSUserBundle所做的.然后他们手动调用updateUser(我相信来自Controller).但是,我希望能够在大多数情况下忘记这一步骤.这就是Doctrine Events的用武之地.

我添加了两个Doctrine事件监听器(都是UserManager):prePersist和preUpdate.

两者都基本上检查我们是否正在处理User对象,然后调用updateUser()以更新用户的密码.preUpdate必须有一个额外的步骤来手动设置新值.

为了确保它触发,User::setPlainPassword()抹了密码,并刷新我的盐(因为plainPassword不是对应的属性,所以只是改变它不会允许用户触发更新前的这是必要的).

很多移动的部分,但现在我已经到位,每当我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);
    }
}
Run Code Online (Sandbox Code Playgroud)
// 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
}
Run Code Online (Sandbox Code Playgroud)
# 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 }
Run Code Online (Sandbox Code Playgroud)