Php 重定向到页面时会话过期
在我的应用程序中,一旦用户登录,就会转到主页,在那里他可以查看自己的详细信息。有一个“编辑配置文件”按钮,用户将被带到可以编辑数据的页面。一旦编辑成功,他将被重定向回主页。但是在这里,它被重定向到登录页面。我认为会话意外过期。如何克服这个问题 //这是我的更新信息控制器Php 重定向到页面时会话过期,php,symfony,Php,Symfony,在我的应用程序中,一旦用户登录,就会转到主页,在那里他可以查看自己的详细信息。有一个“编辑配置文件”按钮,用户将被带到可以编辑数据的页面。一旦编辑成功,他将被重定向回主页。但是在这里,它被重定向到登录页面。我认为会话意外过期。如何克服这个问题 //这是我的更新信息控制器 /** * @Route("/update/{id}", name="update") * @param $id * @param Request $request * @param UserPasswordEncode
/**
* @Route("/update/{id}", name="update")
* @param $id
* @param Request $request
* @param UserPasswordEncoderInterface $passwordEncoder
* @param UserInterface $loggedUser
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function updateUser($id,Request $request, UserPasswordEncoderInterface $passwordEncoder, UrlGeneratorInterface $urlGenerator){
$loggedUser = $this->get('security.token_storage')->getToken()->getUser()->getId();
if ($id == $loggedUser){
$em = $this->getDoctrine()->getManager();
$conn =$em->getConnection();
$user = $em->find(User::class,$id);
$form = $this->createForm(RegisterType::class,$user, [
'validation_groups' => ['update'],
]);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$file = $request->files->get('register')['image'];
if($file){
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move(
$this->getParameter('uploads_dir'), $fileName
);
$user->setImage($fileName);
}
if($user->getPassword() !="") {
$user->setPassword($passwordEncoder->encodePassword($user,$user->getPassword()));
$sql = '
UPDATE user
SET first_name = :firstName, last_name = :lastName, id_number = :idNumber, phone_number = :phoneNumber, address = :address, password = :password
WHERE id= :id
';
$stmt = $conn->prepare($sql);
$stmt->execute(['firstName' => $user->getFirstName(),
'lastName' => $user->getLastName(),
'idNumber' => $user->getIdNumber(),
'phoneNumber' => $user->getPhoneNumber(),
'address' => $user->getAddress(),
'password' => $user->getPassword(),
'id' => $id]);
} else {
$sql = '
UPDATE user
SET first_name = :firstName, last_name = :lastName, id_number = :idNumber, phone_number = :phoneNumber, address = :address
WHERE id= :id
';
$stmt = $conn->prepare($sql);
$stmt->execute(['firstName' => $user->getFirstName(),
'lastName' => $user->getLastName(),
'idNumber' => $user->getIdNumber(),
'phoneNumber' => $user->getPhoneNumber(),
'address' => $user->getAddress(),
'id' => $id]);
}
return new RedirectResponse($urlGenerator->generate('home'));
}
} else {
return new RedirectResponse($urlGenerator->generate('home'));
}
return $this->render('register/update.html.twig', [
'form'=>$form->createView(),
]);
}
//这是注册表类型表单
class RegisterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email',EmailType::class,[
'label'=>'Email',
'required' => false,
'attr'=>['placeholder'=>"Email"]
])
->add('password',RepeatedType::class,[
'type' => PasswordType::class,
'invalid_message' => 'The password fields must match.',
'required' => false,
'options' => ['attr' => ['class' => 'password-field']],
'first_options' => ['label' => 'Password','attr'=>['placeholder'=>"Password"]],
'second_options' => ['label' => 'Confirm Password','attr'=>['placeholder'=>"Confirm Password"]],
])
->add('firstName',TextType::class,['label'=>'First Name', 'attr'=>['placeholder'=>"First Name"]])
->add('lastName',TextType::class,['label'=>'Last Name','attr'=>['placeholder'=>"Last Name"]])
->add('address',TextareaType::class,['required' => false,'label'=>'Address','attr'=>['placeholder'=>"Address"]])
->add('idNumber',TextType::class,['label'=>'NIC Number','attr'=>['placeholder'=>"NIC Number"]])
->add('phoneNumber',TelType::class,['label'=>'Phone Number','attr'=>['placeholder'=>"Phone Number"]])
->add('image',FileType::class,['label'=>'Photo','required'=>false,'attr'=>['hidden'=>"hidden", 'accept'=>"image/jpeg, image/png"]])
->add('save',SubmitType::class,[
'label'=>'Register',
'attr' => [
'class'=>"btn btn-outline-success float-right"
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
//这是我的用户类
class User implements UserInterface{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
* @Assert\Email()
* @Assert\NotBlank()
*/
private $email;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
* @Assert\NotBlank()
*/
private $password;
/**
* @ORM\Column(type="string",length=255)
* @Assert\NotBlank(groups={"update"})
*
*/
private $firstName;
/**
* @ORM\Column(type="string",length=255)
* @Assert\NotBlank(groups={"update"})
*/
private $lastName;
/**
* @ORM\Column(type="string",length=255,nullable=true)
*
*/
private $image;
/**
* @ORM\Column(type="string", nullable=true)
*/
private $address;
/**
* @ORM\Column(type="string",length=10)
* @Assert\Length("10",groups={"update"})
*/
private $phoneNumber;
/**
* @ORM\Column(type="string",length=10)
* @Assert\NotBlank(groups={"update"})
* @Assert\Length("10",groups={"update"})
*/
private $idNumber;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Vehicle", mappedBy="user")
*/
private $vehicle;
/**
* @ORM\OneToOne(targetEntity="App\Entity\Account", inversedBy="user")
*/
private $account;
public function __construct()
{
$this->vehicle = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail( $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername()
{
return (string) $this->email;
}
public function getRoles(): ?array
{
return $this->roles;
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getFirstName()
{
return $this->firstName;
}
public function setFirstName( $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName()
{
return $this->lastName;
}
public function setLastName( $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getAddress()
{
return $this->address;
}
public function setAddress( $address): self
{
$this->address = $address;
return $this;
}
public function getIdNumber()
{
return $this->idNumber;
}
public function setIdNumber( $idNumber): self
{
$this->idNumber = $idNumber;
return $this;
}
public function getPhoneNumber()
{
return $this->phoneNumber;
}
public function setPhoneNumber( $phoneNumber): self
{
$this->phoneNumber = $phoneNumber;
return $this;
}
/**
* @return Collection|Vehicle[]
*/
public function getVehicle(): Collection
{
return $this->vehicle;
}
public function addVehicle(Vehicle $vehicle): self
{
if (!$this->vehicle->contains($vehicle)) {
$this->vehicle[] = $vehicle;
$vehicle->setUser($this);
}
return $this;
}
public function removeVehicle(Vehicle $vehicle): self
{
if ($this->vehicle->contains($vehicle)) {
$this->vehicle->removeElement($vehicle);
// set the owning side to null (unless already changed)
if ($vehicle->getUser() === $this) {
$vehicle->setUser(null);
}
}
return $this;
}
public function getAccount(): ?Account
{
return $this->account;
}
public function setAccount(?Account $account): self
{
$this->account = $account;
return $this;
}
}使用临时字段以预编码/预哈希的形式保存明文密码(请参阅:-字段称为明文密码或类似字段) 我怀疑在设置空密码时会出现一些意外行为,这可能会使会话缓存无效(symfony存储一些用户数据以确定是否必须从数据库重新加载用户,以及如果相关数据发生更改,用户可能会注销)。仅重定向不应注销用户
希望这样就足够了。假设您使用的是默认的投票者和实体安全用户提供程序 这应该适用于Symfony 3.4+,但如果知道您使用的是哪一版本的Symfony,则可以使用其他方法 在每个请求结束时(除非您的防火墙是无状态的),您的 用户对象已序列化到会话。在下个月初 请求时,它将被反序列化,然后传递给您的用户提供程序 “刷新”it(例如,针对新用户的条令查询) 然后,两个用户对象(来自会话的原始对象和 刷新的用户对象)进行“比较”,以查看它们是否“相等”。通过 默认情况下,core AbstractToken类比较 getPassword()、getSalt()和getUsername()方法。如果有的话 这些是不同的,您的用户将被注销。这是一种安全措施 确保恶意用户在以下情况下可以取消身份验证的措施: 核心用户数据更改 但是,在某些情况下,此过程可能会导致意外的后果 身份验证问题。如果您在身份验证方面遇到问题,请 可能是您正在成功进行身份验证,但您立即 在第一次重定向后丢失身份验证 资料来源: 这个问题似乎是由
$user->setPassword($passwordEncoder->encodePassword($user,$user->getPassword()));
它将根据提交的密码生成一个新的哈希密码,从而使用户状态无效,即使用户状态相同
您需要存储用户的纯文本密码,并验证密码是否已更改,并且仅在密码更改时应用密码更改
此外,您的图像
表单设置无效,因为您的用户::$image
需要一个字符串,但表单将上载一个文件
对象(导致实体状态无效或调用文件::\u toString
并更改图像)。您应该使用单独的属性来进行图像上传,并手动绘制视图中的当前图像,或者考虑在窗体中使用数据转换器,而不是在控制器中处理状态更改。见:
将当前的密码
和图像
表单字段替换为普通密码
和上传图像
字段
class RegisterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//...
->add('plainPassword',RepeatedType::class,[
'type' => PasswordType::class,
'invalid_message' => 'The password fields must match.',
'required' => false,
'options' => ['attr' => ['class' => 'password-field']],
'first_options' => ['label' => 'Password','attr'=>['placeholder'=>"Password"]],
'second_options' => ['label' => 'Confirm Password','attr'=>['placeholder'=>"Confirm Password"]],
])
->add('uploadImage',FileType::class,['label'=>'Photo','required'=>false,'attr'=>['hidden'=>"hidden", 'accept'=>"image/jpeg, image/png"]]);
//...
}
您还应该认真考虑使用A,而不是直接从Ortic用户实体管理数据,以防止无效的实体状态。
然后在用户实体中创建属性和getter/setter方法,以存储表单值class User implements UserInterface
{
/**
* @var string
*/
private $plainPassword = '';
/**
* @var File|null
*/
private $uploadImage;
public function getPlainPassword(): string
{
return $this->plainPassword;
}
public function setPlainPassword(string $plainPassword): void
{
$this->plainPassword = $plainPassword;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
$this->plainPassword = null;
}
public function getUploadImage(): ?File
{
return $this->uploadImage;
}
public function setUploadImage(?File $file): void
{
$this->uploadImage = $file;
}
//...
}
由于您使用的是实体管理器和注册表类型字段,因此可以删除手动更新查询。因为$form->handleRequest()
将直接将更改应用于用户对象。我还建议使用Paramconverter从用户对象的实体依赖注入中获益
/**
* @Route("/{user}/update", name="update", requirements={ "user":"\d+" }, methods={"GET","POST"})
* @param User $user
* @param Request $request
* @param UserPasswordEncoderInterface $passwordEncoder
* @param UserInterface $loggedUser
* @return Response
*/
public function updateUser(User $user, Request $request, UserPasswordEncoderInterface $passwordEncoder, UrlGeneratorInterface $urlGenerator): Response
{
$loggedinUser = $this->getUser(); //helper from ControllerTrait
if ($loggedinUser && loggedinUser->getId() === $user->getId()) {
$form = $this->createForm(RegisterType::class,$user, [
'validation_groups' => ['update'],
]);
$currentImage = $user->getImage();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($file = $user->getUploadImage()) {
//this logic should be moved to the Form using a data transformer
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move(
$this->getParameter('uploads_dir'), $fileName
);
$user->setImage($fileName);
}
if ('' !== $user->getPlainPassword() && !$passwordEncoder->isPasswordValid($user->getPassword(), $user->getPlainPassword())) {
//change password only when changed
$user->setPassword($passwordEncoder->encodePassword($user, $user->getPlainPassword()));
$user->eraseCredentials();
}
$em = $this->getDoctrine()->getManager();
$em->flush();
return new RedirectResponse($urlGenerator->generate('home'));
}
return $this->render('register/update.html.twig', [
'form'=>$form->createView(),
]);
}
return new RedirectResponse($urlGenerator->generate('home'));
}
如果您使用的是Symfony<4.1,则需要实现
\Serializable
,并将serialize
和unserialize
方法添加到用户类中,否则整个用户对象将被序列化并在任何更改时失效
class User implements UserInterface, \Serializable
{
//...
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
//$this->roles //(optional)
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
//$this->roles //(optional)
) = unserialize($serialized, array('allowed_classes' => false));
}
}
请显示RegisterType表单类型(php,不一定是twig模板)和用户类。您好,我添加了它们。用户类仍然丢失。对不起。你的解释/回答比我的要广泛得多。荣誉我怀疑序列化,但我手头没有文档。公平地说,OP似乎用空的内容覆盖了旧密码,这将被反序列化。或者,如果用户提供了密码,他会用新的散列覆盖密码。所以不管怎样。。。