我实现了一个简单的密码更改算法。这是代码:
public function postChangePasswordAction(Request $request, $username)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository("TskUserBundle:User")->findOneByUsername($username);
if(!$user)
{
throw $this->createNotFoundException("User not found!");
}
$form = $this->createPasswordChangeForm();
$form->handleRequest($request);
$old_password = $form->get('old_password')->getData();
$new_password = $form->get("new_password")->getData();
if($form->isValid())
{
$old_password_encoded = $this->get("tsk_encoder")->encode($user, $old_password, $user->getSalt());
if($old_password_encoded == $user->getPassword())
{
$user->setPlainPassword($new_password);
$user->setFirstname("rofa");
$em->flush();
return $this->redirect($this->generateUrl("tsk_user_profile", array("username"=>$user->getUsername())));
} else { return new Response($old_password_encoded."\n".$user->getPassword());}
}
return $this->render("TskUserBundle:User:change_password.html.twig", array("username" => $user->getUsername(), "form"=>$form->createView()));
}
我的User
实体是:
<?php
namespace Tsk\UserBundle\Entity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass="Tsk\UserBundle\Repository\UserRepository")
* @ORM\Table(name="users")
* @ORM\HasLifecycleCallbacks
* @Assert\GroupSequence({"FormCreate", "FormEdit", "User"})
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=25, unique=true)
* @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
*/
protected $username;
/**
* @Assert\NotBlank(groups={"FormCreate"})
* @Assert\Length(min="4", minMessage="Password must be 4 characters at least", groups={"FormCreate"})
*/
protected $plain_password;
/**
* @ORM\Column(type="string", length=128)
* @Assert\NotBlank
*/
protected $password;
/**
* @ORM\Column(type="string", length=50, unique=true)
* @Assert\Email(groups={"FormEdit", "FormCreate"});
*/
protected $email;
/**
* @ORM\Column(type="boolean")
*/
protected $is_active;
/**
* @ORM\Column(type="string", length=59)
* @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
*/
protected $firstname;
/**
* @ORM\Column(type="string", length=59)
* @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
*/
protected $lastname;
/**
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users", cascade={"persist"})
* @Assert\NotBlank
*/
protected $roles;
/**
* @ORM\Column(type="string", length=50)
*/
protected $salt;
public function __construct()
{
$this->roles = new ArrayCollection();
$this->salt = md5(uniqid(null, true));
}
/**
* @ORM\PrePersist
*/
public function setIsActiveValue()
{
$this->setIsActive(true);
}
public function getPlainPassword()
{
return $this->plain_password;
}
public function setPlainPassword($plain_password)
{
$this->plain_password = $plain_password;
return $this;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->getIsActive();
}
public function getRoles()
{
return $this->roles->toArray();
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return $this->salt;
}
public function getUsername()
{
return $this->username;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
}
public function serialize()
{
return serialize(array($this->getUsername()));
}
public function unserialize($serialized)
{
list($this->username) = unserialize($serialized);
}
public function getId()
{
return $this->id;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
public function setIsActive($isActive)
{
$this->is_active = $isActive;
return $this;
}
public function getIsActive()
{
return $this->is_active;
}
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
public function getFirstname()
{
return $this->firstname;
}
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
public function getLastname()
{
return $this->lastname;
}
public function addRole(Role $roles)
{
$this->roles[] = $roles;
return $this;
}
public function removeRole(Role $roles)
{
$this->roles->removeElement($roles);
}
}
我有prePersist
和preUpdate
的活动订阅者。当我创建一个新用户时,它正常被解雇。但是,当我只更改密码时,它根本不起作用。
(请注意plain_password
属性是虚拟属性。不会保存在数据库中)
以下是代码:
<?php
namespace Tsk\UserBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Tsk\UserBundle\Entity\User;
use Tsk\UserBundle\Utils\Encoder;
class EncoderSubscriber implements EventSubscriber
{
protected $encoder;
public function __construct(Encoder $ef)
{
$this->encoder = $ef;
}
/**
* Returns an array of events this subscriber wants to listen to.
*/
public function getSubscribedEvents()
{
return array(
'preUpdate',
'prePersist',
);
}
public function preUpdate(LifecycleEventArgs $args)
{
$this->encode($args);
}
public function prePersist(LifecycleEventArgs $args)
{
$this->encode($args);
}
protected function encode(LifecycleEventArgs $args)
{
$user = $args->getEntity();
if($user instanceof User)
{
$encodedPassword = ($this->encoder->encode($user, $user->getPlainPassword(), $user->getSalt()));
$user->setPassword($encodedPassword);
}
}
}
?>
答案 0 :(得分:4)
我也遇到了同样的问题。
原因是Doctrine的工作单元无法计算原始实体和密码更改实体之间的更改集,因为plain_password
不会持久保存到数据库中,因此工作单元忽略plain_password
字段的更改(或者我应该说,工作单元在调用flush之前和调用flush之后发现数据之间没有差异。)
让它发挥作用的方法是在致电password
之前更新EntityManager#flush
。我通过在检测到密码更改时调度UserEvents::CHANGE_PASSWORD
来实现此功能(当然是手动)。使用此方法,如果您只是要更新其中的密码字段,则无需再订阅preUpdate
事件。
您还可以参考FOSUserBundle的实施。