【问题标题】:Sonata、Symfony、密码加密、Doctrine Listener/Subscriber
【发布时间】:2021-01-29 20:26:55
【问题描述】:
  • 信息:我是 Symfony 和 Sonata 的新手

  • 我的目标:只为数据库端加密密码。我将能够清楚地显示此密码。所有这些都适用于实体 Service 中的字段 password

    • 我在尝试什么? : 我尝试创建一个使用bcrypt 加密的 Doctrine Listener

      安全性:

      编码器: 应用\实体\服务:bcrypt


这是我的 App\Doctrine 中的 HashPasswordLisetener.php(在 App\Admin\ServiceAdmin.php 的 configureFormField 函数的 $formMapper 中,我有这样一行 ->add('password')

<?php

namespace App\Doctrine;

use App\Entity\Service;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class HashPasswordListener implements EventSubscriber
{
    private $passwordEncoder;

    public function __construct(UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->passwordEncoder = $passwordEncoder;
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        if (!$entity instanceof Service) {
            return;
        }
        $this->encodePassword($entity);
    }

    public function preUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        if (!$entity instanceof Service) {
            return;
        }
        $this->encodePassword($entity);

        $em = $args->getEntityManager();
        $meta = $em->getClassMetadata(get_class($entity));
        $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
    }

    public function getSubscribedEvents()
    {
        return ['prePersist', 'preUpdate'];
    }

    /**
     * @param Service $entity
     */
    private function encodePassword(Service $entity)
    {
        if (!$entity->getPlainPassword()) {
            return;
        }
        $encoded = $this->passwordEncoder->encodePassword(
            $entity,
            $entity->getPlainPassword()
        );
        $entity->setPassword($encoded);
    }
}

这里是 App\Entity 中的 Service 实体

<?php

namespace App\Entity;

use App\Admin\AbstractAdmin;
use App\Repository\ServiceRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass=ServiceRepository::class)
 */
class Service implements UserInterface
{

    use TimestampableEntity;

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $password;

    /**
     * @ORM\Column(type="text", nullable=true, length=255)
     */
    private $comment;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $identifier;

    private $plainPassword;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    public function getComment(): ?string
    {
        return $this->comment;
    }

    public function setComment(?string $comment): self
    {
        $this->comment = $comment;

        return $this;
    }

    public function getIdentifier(): ?string
    {
        return $this->identifier;
    }

    public function setIdentifier(string $identifier): self
    {
        $this->identifier = $identifier;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    /**
     * @param mixed $plainPassword
     */
    public function setPlainPassword($plainPassword): void
    {
        $this->plainPassword = $plainPassword;
        $this->password = null;
    }

    public function getRoles()
    {
        return array('ROLE_USER');
    }

    public function getSalt()
    {
    }

    public function eraseCredentials()
    {
        $this->plainPassword = null;
    }

    public function getUsername()
    {
        return $this->identifier;
    }
}

还有我的 sonata_admin.yaml :

app.doctrine.hash_password_listener:
    class: App\Doctrine\HashPasswordListener
    autowire : true
    tags:
        - { name: doctrine.event_subscriber, connection: 'default' }

  • 它给我的结果:

  • 我的问题: 我知道函数 encodePassword 正在等待第一个参数(而不是我的实体)中的 UserInterface 和第二个参数中要加密的密码,但我不明白谁使用这个 UserInterface ?我应该在哪里称呼它?为拿到它,为实现它 ?发送它?

我想我提供了很多细节,但如果我忘记了什么,请随时注意我^^ 感谢您至少花时间阅读。

【问题讨论】:

  • UserInterface 用于整个auth system。只需让您的 Service 实体实现它。但如果你不想,你可以用PasswordEncoder代替UserPasswordEncoder,见EncoderFactory
  • 好的,我会试试的
  • 像以前一样输入UserPasswordEncoderInterface
  • 这不一定是username字段,只是一种唯一标识用户帐户的方法。我猜在您的情况下,只需删除 abstract 关键字并将其设为 return $this-&gt;identifier;
  • 您确定正在调用侦听器吗?另一方面,setPassword 只能由侦听器调用,应该无法在其中得到明文密码,您的表单映射是否正确?

标签: symfony doctrine-orm doctrine password-encryption sonata


【解决方案1】:

我遇到了问题,但我正在编写 Hashing 类型的代码,但我正在寻找一种加密方法。但这是我处理有效哈希的方法:

  • 第 1 步:在我的服务实体中实现 UserInterface

  • 第 2 步:将 UserPasswordEncoder 替换为 Listener 中的 UserPasswordEncoderInterface

  • 第 2.5 步:添加了 UserInterface 所需的所有功能,例如 eraseCredidential getSalt().. 请参阅:this 了解更多详情

  • 第 3 步:添加 getUsername 其中 return this-&gt;identifier

  • 第 4 步:在表单字段中使用 plainPasswords 而不是 password

  • 第 5 步:添加提供者:

    app_user:

      entity:
      class: 'App\Entity\Service'
      property: 'identifier'
    
  • 第6步:使用TextType::class作为plainPassword表单字段类型+修复useSymfony\Component\Form\Extension\Core\Type\TextType

  • 第 7 步:工作

(特别感谢@msg,他帮了我很多)

【讨论】:

    猜你喜欢
    • 2012-06-18
    • 1970-01-01
    • 2019-07-11
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多