我正在尝试在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);
}
}
正如您在我的评论中所看到的,至少有几个原因导致上述代码不起作用。我只想发布有关这些特定问题的具体问题,但也许我正在咆哮错误的树。这就是我问整体问题的原因。
那么,我该如何验证用户的密码?
答案 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的表单并完成了控制器中的所有逻辑。