简体   繁体   English

在表单类型中使用Symfony2 UserPassword验证程序

[英]Using Symfony2 UserPassword validator in form type

Im trying to use a specific validator in a form. 我试图在表单中使用特定的验证器。

That form is for an user to redefine his password, he must also enter his current password. 该表格供用户重新定义其密码,他还必须输入其当前密码。 For that I use a built in validator from symfony 为此,我使用来自symfony的内置验证器

in my form: 以我的形式:

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

and the form type looks like that: 表单类型如下所示:

 /**
 * @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
        ))
    ;
}

I know in my controller I could just get the data form, get the currentpassword value, call the security.encoder_factory, etc but that validator looked handy. 我知道我可以在控制器中获取数据表单,获取当前密码值,调用security.encoder_factory等,但是该验证程序很方便。

my problem is that the form always return an error (here: 'you wot m8?') just like I had entered the wrong current password. 我的问题是,表单总是返回错误(在这里:“ you wot m8?”),就像我输入了错误的当前密码一样。

Any idea what I am doing wrong? 知道我在做什么错吗?

I know this answer is coming a few years late, but as I was stumbleing ybout the same Problem I want to present my solution: 我知道这个答案要晚几年了,但是当我偶然遇到同样的问题时,我想提出我的解决方案:

The problem is that there was a connection in my case between the $user Entity I used for FormMapping and the User that comes form the security.context . 问题是在我的情况下,我用于FormMapping的$user实体与来自security.contextUser存在联系。

See following: (PasswordChange - Controller) 参见以下内容:(PasswordChange-控制器)

    $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(...);

The isValid() function is triggering the UserPassword Constraint Validator: 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);
    }
}

The line of interest is: if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) 感兴趣的行是: if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt()))

In my case the $user->getPassword() was giving back the new Password I just entered in the form as my new Password. 在我的情况下, $user->getPassword()退还我刚刚在表单中输入的新密码作为新密码。 Thats why the test allways failed! 这就是为什么测试始终失败的原因! I did not understand why there could be a connection between the User in the tokenStorage and the one that I loaded from my Database. 我不明白为什么令牌存储中的用户与我从数据库中加载的用户之间可能存在连接。 It feels like both Objects (MyDatabase one and the tokenStorage one) share the same processor address and are actually the same... 感觉像两个对象(MyDatabase一个和tokenStorage一个)共享相同的处理器地址,并且实际上是相同的...

Weird! 奇怪的!

My solution was to also decouple the (new)password field in ChangePasswordType from the EntityMapping: See 我的解决方案是还要将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')

The line of interest is 'mapped' => false, 感兴趣的行是'mapped' => false,

By this, the new password entered in the form will not be automatically mapped to the given $user Entity. 这样,在表单中输入的新密码将不会自动映射到给定的$user实体。 Instead you now need to grab it from the form . 相反,您现在需要从form获取它。 See 看到

    $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();
    }

A bit of a workaround for problem what I could not fully understand. 对于我无法完全理解的问题,有一些解决方法。 If anyone can explain the connection between the Database Object and the security.context Object I'd be glad to hear it! 如果有人能解释数据库对象和security.context对象之间的连接,我将很高兴听到它!

I had the same problem and, after lot of research and practical tests, this is the solution I used: 我遇到了同样的问题,经过大量的研究和实践测试,这是我使用的解决方案:

  1. keep User Entity as it is (no changes) 保持用户实体不变(无更改)
  2. Define a new entity (ChangePassword), with 2 fields, as MODEL in directory 'Model' in 'Form' Folder, as described in symfony docs: [ https://symfony.com/doc/current/reference/constraints/UserPassword.html][1] ) 定义一个新的实体(ChangePassword),该实体具有2个字段,作为'Form'文件夹中'Model'目录中的MODEL,如symfony文档所述:[ https://symfony.com/doc/current/reference/constraints/UserPassword。 html] [1]
    • Field oldPassword: add a constraint: @SecurityAssert\\UserPassword(message = "old pass wrong.....") 字段oldPassword:添加约束:@SecurityAssert \\ UserPassword(message =“旧密码错误.....”)
    • Field: newPassword 栏位:newPassword
      (NB: if you dont define this entity as model, then you can not map it, later in the form, unless you create it throw Doctrine, and that is not our purpose) (注意:如果您不将此实体定义为模型,则无法在以后的形式中映射它,除非您创建该实体以抛出“学说”,这不是我们的目的)
  3. Define the form 'ChangePasswordType' for these fields (newPasword could be RepeatedType for password confirmation). 为这些字段定义格式“ ChangePasswordType”(newPasword可以为RepeatedType以进行密码确认)。 The mapping as 'true' MUST be maintained for automatic validation of oldPassword, throw SecurityAssert defined above, and for catching these fields later in the controller 必须保持映射为“ true”,以自动验证oldPassword,抛出上面定义的SecurityAssert,并稍后在控制器中捕获这些字段。

  4. In the controller (changeCurrentUserPasswordAction,... or whatever Action), declare a new ChangePassword Entity and associate it with the form to create ('ChangePasswordType) 在控制器中(changeCurrentUserPasswordAction,...或其他操作),声明一个新的ChangePassword实体并将其与要创建的表单相关联('ChangePasswordType)

  5. Now you can execute and see that wrong password could not be passed for oldPassword (since it should be equal to the actual password of authentificated user) 现在,您可以执行并看到无法为oldPassword传递错误的密码(因为该密码应该与已认证用户的实际密码相同)
  6. Finally, in the controller, and when the form is submitted, get the value of the new password entered in the form (using $newpass = $form->getData()->getPassword(); for example) and encode it and set it as the new password $user->setPassword($newpass) before the flush. 最后,在控制器中,提交表单后,获取在表单中输入的新密码的值(例如,使用$ newpass = $ form-> getData()-> getPassword();)对其进行编码和设置刷新前将其作为新密码$ user-> setPassword($ newpass)。

I hope this can help someone... 我希望这可以帮助某人...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM