duandao3265 2013-02-12 01:49
浏览 58
已采纳

Symfony2表单在提交时编码密码

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.

  • 写回答

1条回答 默认 最新

  • dougu2006 2013-02-12 03:38
    关注

    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 }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 要这个数学建模编程的代码 并且能完整允许出来结果 完整的过程和数据的结果
  • ¥15 html5+css和javascript有人可以帮吗?图片要怎么插入代码里面啊
  • ¥30 Unity接入微信SDK 无法开启摄像头
  • ¥20 有偿 写代码 要用特定的软件anaconda 里的jvpyter 用python3写
  • ¥20 cad图纸,chx-3六轴码垛机器人
  • ¥15 移动摄像头专网需要解vlan
  • ¥20 access多表提取相同字段数据并合并
  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算
  • ¥15 powerbuilder中的datawindow数据整合到新的DataWindow