Symfony2表单在提交时对密码进行编码 [英] Symfony2 Form Encode Password on Submit

查看:50
本文介绍了Symfony2表单在提交时对密码进行编码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为自定义User对象构建了UserType.字段之一是密码.

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

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

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

谢谢.

解决方案

因此,我进行了一些挖掘和查找,并从FOSUserBundle中获取了一些提示.

要回答我的第一个问题,似乎都不是最好的选择,因此使第二个问题无济于事.

我最终要做的事情:

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

这或多或少是FOSUserBundle所做的.然后,他们手动调用updateUser(我相信来自Controller).但是,我希望能够在很大程度上省却这一步骤.这就是教义事件的出现.

我添加了两个教义事件侦听器(都添加到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 }

I've built a UserType for a custom User object. One of the fields is a password.

Ideally, I'd like it to encode the password when I submit the form as a data transformer/related instead of having to handle this in the controller. However, it's a salted password, so this poses a problem because I like to regenerate the salt every time I generate the password. I don't know of a way to get this extra value in to my DataTransformer.

So, I basically have two questions:

  • Is it a good/bad idea to have it do the encoding as part of the UserType (AbstractType), or should I handle it in the Controller?
  • How would I pass in the required information to my DataTransformer to make this possible?

Thanks.

解决方案

So, I did some digging and looking, and took some cues from FOSUserBundle.

To answer my first question, it looks like neither is the best option, thus rendering the second question moot.

What I ended up doing:

  • Add a new field to my User entity, $plainPassword. I then changed the mapping of my UserType to this field, instead of $password directly.
    • Be sure to blank out $plainPassword in User::eraseCredentials
  • Made a new service called UserManager. In this, I gave it a function updateUser() which handles the actual encoding of the password (no longer Controller handled, yay!).

That's more or less what FOSUserBundle does. They then call updateUser manually (I believe from the Controller). However, I want to be able to forget about this step for the most part. That's where Doctrine Events came in to play.

I added two Doctrine Event listeners (both to UserManager): prePersist and preUpdate.

Both basically check if we are dealing with a User object, then call updateUser() to update the User's password. preUpdate had to have an extra step of manually setting the new value.

To make sure it triggers, User::setPlainPassword() wipes out the password and refreshes my salt (this is necessary because plainPassword isn't a mapped property, so just changing it won't allow the User to trigger preUpdate).

A lot of moving pieces, but now that I have it in place, whenever I use User::setPlainPassword() to change the plaintext password (regardless where), it will now properly encode and save the actual password values. Woot!


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 }

这篇关于Symfony2表单在提交时对密码进行编码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆