首页 文章

用于迁移旧密码的自定义Symfony FOSUserBundle服务

提问于
浏览
0

我正在尝试将自己的旧密码服务插入Symfony3,以便从旧数据库表中被动迁移用户 .

遗留系统的密码具有在所有成员中使用的相同硬编码的 $salt 变量(因此,我的FOSUserBundle表当前对于要迁移的所有成员,其盐列为空) .

遗留方法使用:

sha1($salt1.$password.$salt2)

新方法是Symfony的FOSUserBundle标准bcrypt哈希 .

我正在尝试实现它,以便当旧用户首次登录时,Symfony将尝试:

  • 使用FOSUserBundle的标准bcrypt方法登录 .

  • 如果#1没有成功,那么尝试遗留算法 .

  • 如果#2成功进行密码哈希,数据库表中的salt将更新为符合标准FOSUserBundle方法

我一直在阅读如何插入服务以使其工作,我认为我在下面的理论似乎是正确的 - 如果没有任何更正/指导将被赞赏,因为我无法测试它!

However, I'm unsure how I should go about connecting it all into Symfony so that the normal FOSUserBundle processes will carry out steps 2 and 3 if step 1 fails

services.yml:

parameters:
    custom-password-encoder:
        class: AppBundle\Security\LegacyPasswordEncoder

security.yml:

security:
    encoders:
        #FOS\UserBundle\Model\UserInterface: bcrypt       Commented out to try the following alternative to give password migrating log in
         FOS\UserBundle\Model\UserInterface: { id: custom-password-encoder }

BCryptPasswordEncoder (standard FOSUserBundle):

class BCryptPasswordEncoder extends BasePasswordEncoder
{
    /* .... */

    /**
     * {@inheritdoc}
     */
    public function encodePassword($raw, $salt)
    {
        if ($this->isPasswordTooLong($raw)) {
            throw new BadCredentialsException('Invalid password.');
        }

        $options = array('cost' => $this->cost);

        if ($salt) {
            // Ignore $salt, the auto-generated one is always the best
        }

        return password_hash($raw, PASSWORD_BCRYPT, $options);
    }


    /**
     * {@inheritdoc}
     */
    public function isPasswordValid($encoded, $raw, $salt)
    {
        return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded);
    }
}

LegacyPasswordEncoder:

namespace AppBundle\Security;
use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;

class LegacyPasswordEncoder extends BasePasswordEncoder
{
    /**
     * {@inheritdoc}
     */
    public function encodePassword($raw,$salt)
    {
        if ($this->isPasswordTooLong($raw)) {
            throw new BadCredentialsException('Invalid password.');
        }

        list($salt1,$salt2) = explode(",",$salt);
        return sha1($salt1.$raw.$salt2);
    }

    /**
     * {@inheritdoc}
     */
    public function isPasswordValid($encoded, $raw, $salt)
    {
        list($salt1,$salt2) = explode(",",$salt);
        return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded,sha1($salt1.$raw.$salt2));

    }
}

2 回答

  • 0

    首先将用户类映射到所需的编码器:

    security:
      hide_user_not_found: false
      encoders:
        Cerad\Bundle\UserBundle\Entity\User: # Replace with your user class
            id: cerad_user.user_encoder # Replace with the service id for your encoder
    

    这应该足以让您的编码器插入 .

    然后,您需要通过扩展BCryptPasswordEncoder并覆盖isPasswordValid方法来实际编写自定义编码器 . 当然,为它创建一个服务 . 有很多东西需要学习 .

    如何调用BcryptPasswordEncorder后跟LegacyPasswordEncoder?你没有 . 至少不是直接的 . Symfony没有链密码编码器 . 相反,编写自己的编码器并自己实现链接 .

    class MyEncoder extends BCryptPasswordEncoder
    {
      function isPasswordValid($encoded,$raw,$salt)
      {
        // Check the bcrypt
        if (parent::isPasswordValid($encoded,$raw,$salt)) return true;
    
        // Copied from legacy
        list($salt1,$salt2) = explode(",",$salt);
        return 
          !$this->isPasswordTooLong($raw) &&
           $this>comparePasswords($encoded,sha1($salt1.$raw.$salt2));
    

    并确保在服务而不是参数下定义编码器 . 还要确保将成本(默认值为13)作为构造函数参数传递 .

  • 1

    您的问题的解决方案是使用Symfony功能允许根据用户动态更改密码哈希算法:https://symfony.com/doc/current/cookbook/security/named_encoders.html

    这样,您可以将任何未迁移的用户标记为使用旧算法 . 然后,在更新密码时,您将在保存用户之前重置正在使用的算法,以便使用新的更强算法对新密码进行哈希处理

相关问题