Symfony2表单在提交时编码密码

时间:2013-02-12 01:49:23

标签: php forms symfony

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

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

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

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

感谢。

1 个答案:

答案 0 :(得分:5)

所以,我做了一些挖掘和寻找,并从FOSUserBundle中获取了一些线索。

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

我最终做了什么:

  • 向我的用户实体$plainPassword添加新字段。然后我将UserType的映射更改为此字段,而不是直接更改$ password。
    • 请务必在$plainPassword
    • 中删除User::eraseCredentials
  • 制作了一个名为UserManager的新服务。在这里,我给它一个函数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 }