在Symfony2中验证密码

时间:2012-04-01 15:50:08

标签: symfony

我正在尝试在Symfony2中整理更改密码功能。我有一个“当前密码”字段,一个“新密码”字段和一个“确认新密码”字段,我目前关注的部分是验证“当前密码”字段。

(顺便说一句,我现在意识到像FOSUserBundle这样的东西会为我处理很多这些事情,但我已经根据官方的Symfony文档构建了我的身份验证系统,而且我不知道现在没时间重做我的所有身份验证代码。)

我正在想象/希望我能做的是创建一个类似这样的验证回调:

// Entity/User.php

public function currentPasswordIsValid(ExecutionContext $context)
{
  $currentPassword = $whatever; // whatever the user submitted as their current password
  $factory = $this->get('security.encoder_factory'); // Getting the factory this way doesn't work in this context.
  $encoder = $factory->getEncoder($this);
  $encryptedCurrentPassword = $encoder->encodePassword($this->getPassword(), $this->getSalt());

  if ($encyptedCurrentPassword != $this->getPassword() {
    $context->addViolation('Current password is not valid', array(), null);
  }
}

正如您在我的评论中所看到的,至少有几个原因导致上述代码不起作用。我只想发布有关这些特定问题的具体问题,但也许我正在咆哮错误的树。这就是我问整体问题的原因。

那么,我该如何验证用户的密码?

4 个答案:

答案 0 :(得分:11)

自Symfony 2.1以后就有built-in constraint


首先,您应该创建一个custom validation constraint。您可以将验证器注册为服务,并在其中注入您需要的任何内容。

其次,由于您可能不希望将当前密码的字段添加到User类,只是为了将约束粘贴到它,您可以使用所谓的form model。实质上,您在Form\Model命名空间中创建一个包含当前密码字段和对用户对象的引用的类。您可以将自定义约束粘贴到该密码字段。然后根据此表单模型创建密码更改表单类型。

以下是我的一个项目的约束示例:

<?php
namespace Vendor\Bundle\AppBundle\Validator\Constraints\User;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class CurrentPassword extends Constraint
{
    public $message = "Your current password is not valid";

    /**
     * @return string
     */
    public function validatedBy()
    {
        return 'user.validator.current_password';
    }
}

及其验证器:

<?php
namespace Vendor\Bundle\AppBundle\Validator\Constraints\User;

use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use JMS\DiExtraBundle\Annotation\Validator;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\DiExtraBundle\Annotation\Inject;

/**
 * @Validator("user.validator.current_password")
 */
class CurrentPasswordValidator extends ConstraintValidator
{
    /**
     * @var EncoderFactoryInterface
     */
    private $encoderFactory;

    /**
     * @var SecurityContextInterface
     */
    private $securityContext;

    /**
     * @InjectParams({
     *     "encoderFactory"  = @Inject("security.encoder_factory"),
     *     "securityContext" = @Inject("security.context")
     * })
     *
     * @param EncoderFactoryInterface  $encoderFactory
     * @param SecurityContextInterface $securityContext
     */
    public function __construct(EncoderFactoryInterface  $encoderFactory,
                                SecurityContextInterface $securityContext)
    {
        $this->encoderFactory  = $encoderFactory;
        $this->securityContext = $securityContext;
    }

    /**
     * @param string     $currentPassword
     * @param Constraint $constraint
     * @return boolean
     */
    public function isValid($currentPassword, Constraint $constraint)
    {
        $currentUser = $this->securityContext->getToken()->getUser();
        $encoder = $this->encoderFactory->getEncoder($currentUser);
        $isValid = $encoder->isPasswordValid(
            $currentUser->getPassword(), $currentPassword, null
        );

        if (!$isValid) {
            $this->setMessage($constraint->message);
            return false;
        }

        return true;
    }
}

我使用我的Blofwish password encoder bundle,所以我不会将盐作为$encoder->isPasswordValid()方法的第三个参数传递,但我认为您将能够根据自己的需要调整此示例。< / p>

另外,我使用JMSDiExtraBundle来简化开发,但您当然可以使用经典的服务容器配置方式。

答案 1 :(得分:6)

在Symfony 2.1中,您可以使用内置验证器: http://symfony.com/doc/master/reference/constraints/UserPassword.html

例如,在表单构建器中:

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

// mapped=>false (new in 2.1) is to let the builder know this is not an entity field
->add('currentpassword', 'password', array('label'=>'Current password', 'mapped' => false, 'constraints' => new UserPassword()))

显然现在有一个验证程序存在错误,因此现在可能或现在可能正常工作 https://github.com/symfony/symfony/issues/5460

答案 2 :(得分:0)

FOSUserBundle使用ModelManager类,该类与基础Model分开。您可以查看他们的implementation

答案 3 :(得分:-3)

我最终切断了戈尔迪结。我绕过了所有Symfony的表单并完成了控制器中的所有逻辑。