2

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

理想情况下,当我将表单作为数据转换器/相关提交时,我希望它对密码进行编码,而不必在控制器中处理它。但是,它是一个加盐密码,所以这会带来一个问题,因为我喜欢在每次生成密码时重新生成盐。我不知道有什么方法可以将这个额外的值输入到我的 DataTransformer 中。

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

  • 让它作为 UserType (AbstractType) 的一部分进行编码是一个好/坏的主意,还是应该在控制器中处理它?
  • 我如何将所需的信息传递给我的 DataTransformer 以使这成为可能?

谢谢。

4

1 回答 1

5

因此,我进行了一些挖掘和查找,并从 FOSUserBundle 中获得了一些线索。

要回答我的第一个问题,看起来两者都不是最好的选择,因此使第二个问题变得毫无意义。

我最终做了什么:

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

这或多或少是 FOSUserBundle 所做的。然后他们手动调用 updateUser(我相信来自控制器)。但是,我希望能够在很大程度上忘记这一步。这就是 Doctrine Events 发挥作用的地方。

我添加了两个 Doctrine 事件侦听器(都到 UserManager):prePersist 和 preUpdate。

两者都基本上检查我们是否正在处理用户对象,然后调用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 }
于 2013-02-12T03:38:39.247 回答