【问题标题】:Using Symfony2 UserPassword validator in form type在表单类型中使用 Symfony2 UserPassword 验证器
【发布时间】:2014-03-29 09:01:38
【问题描述】:

我正在尝试在表单中使用特定的验证器。

该表格供用户重新定义他的密码,他还必须输入他当前的密码。 为此,我使用了 symfony 的内置验证器

以我的形式:

use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;

表单类型如下所示:

 /**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('currentpassword', 'password', array('label'=>'Current password',
            'mapped' => false,
            'constraints' => new UserPassword(array('message' => 'you wot m8?')),
            'required' => true
        ))
        ->add('password', 'repeated', array(
            'first_name' => 'new',
            'second_name' => 'confirm',
            'type' => 'password',
            'required' => true
        ))
    ;
}

我知道在我的控制器中我可以获取数据表单、获取当前密码值、调用 security.encoder_factory 等,但验证器看起来很方便。

我的问题是表单总是返回错误(这里:'you wot m8?'),就像我输入了错误的当前密码一样。

知道我做错了什么吗?

【问题讨论】:

  • 您找到解决方案了吗?
  • @Daniel 不,我必须自己进行验证
  • 我现在遇到了同样的问题。有人有想法吗?

标签: php validation symfony passwords formbuilder


【解决方案1】:

我知道这个答案要晚几年才出现,但是当我遇到同样的问题时,我想提出我的解决方案:

问题是在我用于 FormMapping 的 $user 实体和来自 security.contextUser 之间存在连接。

请参阅以下内容:(密码更改 - 控制器)

    $username = $this->getUser()->getUsername();
    $user = $this->getDoctrine()->getRepository("BlueChordCmsBaseBundle:User")->findOneBy(array("username"=>$username));
    // Equal to $user = $this->getUser();

    $form = $this->createForm(new ChangePasswordType(), $user);
    //ChangePasswordType equals the one 'thesearentthedroids' posted


    $form->handleRequest($request);
    if($request->getMethod() === "POST" && $form->isValid()) {
        $manager = $this->getDoctrine()->getManager();
        $user->setPassword(password_hash($user->getPassword(), PASSWORD_BCRYPT));
        [...]
    }

    return array(...);

isValid() 函数正在触发 UserPassword 约束验证器:

public function validate($password, Constraint $constraint)
{
    if (!$constraint instanceof UserPassword) {
        throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
    }

    $user = $this->tokenStorage->getToken()->getUser();

    if (!$user instanceof UserInterface) {
        throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
    }

    $encoder = $this->encoderFactory->getEncoder($user);

    if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
        $this->context->addViolation($constraint->message);
    }
}

感兴趣的线路是:if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt()))

在我的情况下,$user->getPassword() 将我刚刚在表单中输入的新密码作为我的新密码返回。 这就是为什么测试总是失败! 我不明白为什么 tokenStorage 中的用户与我从数据库加载的用户之间存在连接。 感觉就像两个对象(MyDatabase 一个和 tokenStorage 一个)共享相同的处理器地址,实际上是相同的......

奇怪!

我的解决方案是将 ChangePasswordType 中的(新)密码字段与 EntityMapping 分离:参见

        ->add('currentpassword', 'password', array('label'=>'Current password', 'mapped' => false, 'constraints' => new UserPassword()))
        ->add('password', 'repeated', array(
            'mapped'          => false,
            'type'            => 'password',
            'invalid_message' => 'The password fields must match.',
            'required'        => true,
            'first_options'   => array('label' => 'Password'),
            'second_options'  => array('label' => 'Repeat Password'),
            ))
        ->add('Send', 'submit')
        ->add('Reset','reset')

感兴趣的线路是'mapped' => false,

这样,在表单中输入的新密码将不会自动映射到给定的$user 实体。相反,您现在需要从form 获取它。见

    $form->handleRequest($request);
    if($request->getMethod() === "POST" && $form->isValid()) {
        $data = $form->getData();
        $manager = $this->getDoctrine()->getManager();
        $user->setPassword(password_hash($data->getPassword(), PASSWORD_BCRYPT));
        $manager->persist($user);
        $manager->flush();
    }

一些我无法完全理解的问题的解决方法。如果有人能解释数据库对象和 security.context 对象之间的联系,我会很高兴听到它!

【讨论】:

  • 谢谢,这对我也有帮助。为了删除我用于 FormMapping 的 $user 实体和来自 security.context 的用户之间的连接,我为 UserPasswordChange 模型添加了一个具有 oldPassword 和 newPassword 属性的新模型,并使用它们来在控制器中创建表单(不是$this->getUser() 就像我之前做的那样)
【解决方案2】:

我遇到了同样的问题,经过大量研究和实际测试,这是我使用的解决方案:

  1. 保持用户实体不变(无更改)
  2. 定义一个新实体 (ChangePassword),有 2 个字段,在 'Form' 文件夹中的目录 'Model' 中作为 MODEL,如 symfony 文档中所述:[https://symfony.com/doc/current/reference/constraints/UserPassword.html][1])
    • 字段 oldPassword:添加约束:@SecurityAssert\UserPassword(message = "old pass wrong.....")
    • 字段:新密码
      (注意:如果您不将此实体定义为模型,那么您不能在稍后的表单中映射它,除非您创建它抛出 Doctrine,这不是我们的目的)
  3. 为这些字段定义表单“ChangePasswordType”(newPasword 可以是 RepeatedType 用于密码确认)。必须将映射保持为“真”以自动验证 oldPassword、抛出上面定义的 SecurityAssert 以及稍后在控制器中捕获这些字段

  4. 在控制器(changeCurrentUserPasswordAction,...或任何Action)中,声明一个新的ChangePassword Entity并将其与要创建的表单相关联('ChangePasswordType)

  5. 现在你可以执行,看到oldPassword不能传递错误的密码(因为它应该等于认证用户的实际密码)
  6. 最后,在控制器中,当表单提交时,获取表单中输入的新密码的值(例如使用$newpass = $form->getData()->getPassword(); )并编码并将其设置为刷新前的新密码 $user->setPassword($newpass)。

我希望这可以帮助某人......

【讨论】:

    猜你喜欢
    • 2014-07-23
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多