我正在一个允许用户下载软件包的网站。
我有一个Packages实体,其中包含属性(标题,注释,...)和File属性。这些文件的上传要感谢VichUploaderBundle。
我也有两个User表。一个供那些在本地连接到站点(站点上的登录表单)和CAS身份验证的人使用。
当我在CAS中进行身份验证并想要访问显示要下载的软件包的页面时,遇到此错误:
“ Symfony \ Component \ HttpFoundation \ File \ File”的序列化不是 允许
但是,当我在本地连接时,没有这样的问题。我不明白为什么会有这个错误。
这是我的代码:
CAS身份验证:成功:
/**
* Traitement personnalisé après récupération du token
*
* Il est possible d'enrichir le token (attributs...) ou d'effectuer des contrôles supplémentaire
*
* @param $token
* Token d'authification généré
*
* @return null
*/
public function onSuccess($token){
$mail = $this->ai->getMail();
$nomComplet = $this->ai->getCompletName();
$rne = $this->ai->getRne();
$token->setAttribute('mail', $mail);
$token->setAttribute('nomComplet', $nomComplet);
$token->setAttribute('rne', $rne);
$token->setAttribute('typeAuth','cas');
$user = $this->checkBDD($mail); //I retrieve the user object
$token->setAttribute('user',$user);
}
User对象作为属性放置在令牌中。
他进入了个人资料,其中必须包含他的信息,以及代表他已经下载的软件包的表格。
/**
* @Route ("/profile", name="user_profile")
*/
public function profileAction()
{
$token = $this->get('security.token_storage')->getToken();
//dump($token->getAttribute('nomComplet'));
$user = $token->getAttribute('user');
return $this->render('@Pages/cas/profile.html.twig');
}
Profile.html.twig:
{% extends "base.html.twig" %}
{% block body %}
{% set user = app.getToken().getAttribute('user') %}
{% dump(user) %}
{% dump(app.getToken().getAttribute('nomComplet')) %}
<div class="container">
<h1><u>{{ user.mail }}</u></h1><br/>
<h2>Profil</h2>
<br/>
<table class="table">
<tbody>
<tr>
<th scope="row">Nom d'utilisateur</th>
<td>{{app.user.username}}</td>
</tr>
<tr>
<th scope="row">Email</th>
<td>{{user.mail}}</td>
</tr>
<tr>
<th scope="row">Téléchargements restants</th>
<td>4</td>
</tr>
</tbody>
</table>
<br/><br/><br/>
<h3> Liste des packages téléchargés </h3> <br/>
<table class="table table-stripped">
<thead>
<tr>
<th>Titre</th>
<th>Package</th>
<th>Notice</th>
<th>Commentaire</th>
</tr>
</thead>
<tbody>
{% for unPackage in user.packages %}
<tr>
<td> {{unPackage.titre}} </td>
<td> {{unPackage.urlPaquet}} </td>
<td> {{unPackage.urlNotice}} </td>
<td> {{unPackage.commentaire}} </td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{path('connexion_index')}}" class="btn btn-primary">Retour à l'accueil</a>
<a href="{{ path('deconnexion') }}" class="btn btn-primary">Déconnexion</a><br/><br/><br/><br/>
</div>
在树枝中,我们检索令牌中的User属性,并显示其信息,包括其包装。 (用户具有一个ArrayCollection的packages属性,并且在ManyToMany中与Package实体相关联)
我的用户实体:
<?php
namespace Site\PagesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Site\PagesBundle\Security\Traits\traitUser;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* UserCas
*
* @ORM\Table(name="user_cas")
* @ORM\Entity(repositoryClass="Site\PagesBundle\Repository\UserCasRepository")
* @UniqueEntity("mail")
*/
class UserCas
{
use traitUser;
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var int
*
* @ORM\Column(name="nbTelechargementsAuto", type="integer", nullable=true)
*/
private $nbTelechargementsAuto;
/**
* @var bool
*
* @ORM\Column(name="enabled", type="boolean")
*/
private $enabled;
/**
* @ORM\Column(name="mail", type="string")
*/
private $mail;
/**
* @var \Doctrine\Common\Collections\Collection
* @ORM\ManyToMany(targetEntity="Paquet")
* @ORM\JoinTable(name="paquetsDDLUserCas")
* @ORM\JoinColumn(nullable=false)
*/
private $packages;
/**
* Constructor
*/
public function __construct()
{
$this->packages = new ArrayCollection();
$this->setEnabled(true);
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getMail()
{
return $this->mail;
}
public function setMail($mail)
{
$this->mail = $mail;
}
/**
* Set enabled
*
* @param boolean $enabled
*
* @return UserCas
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
public function isEnabled()
{
return $this->enabled;
}
}
我的包裹实体:
<?php
namespace Site\PagesBundle\Entity;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Site\PagesBundle\Entity\Paquet;
use Site\PagesBundle\Entity\TypeUser;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\Common\Collections\ArrayCollection;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Paquet
*
* @ORM\Table(name="paquet")
* @ORM\Entity(repositoryClass="Site\PagesBundle\Repository\PaquetRepository")
* @Vich\Uploadable
*/
class Paquet
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \Doctrine\Common\Collections\Collection
* @ORM\ManyToMany(targetEntity="TypeUser")
* @ORM\JoinTable(name="Packages_des_TypesUser")
* @ORM\JoinColumn(nullable=false)
*/
private $typeUser;
public function __construct()
{
$this->typeUser = new ArrayCollection();
}
/**
* Get TypeUser
*
* @return Site\PagesBundle\Entity\TypeUser
*/
public function getTypeUser()
{
return $this->typeUser;
}
public function deleteTypeFromTypesUser(TypeUser $type)
{
$this->typeUser->removeElement($type);
}
/**
* Set typeUser
*
* @param Site\PagesBundle\Entity\TypeUser $typeUser
*
* @return Paquet
*/
public function setTypeUser(Site\PagesBundle\Entity\TypeUser $typeUser)
{
$this->typeUser = $typeUser;
return $this;
}
/**
* @var string
*
* @ORM\Column(name="titre", type="string", length=255)
* @Assert\Length(min=5, max=255, minMessage="Le titre doit comporter au minimum 5 caractères")
*/
private $titre;
/**
* @var string
*
* @ORM\Column(name="urlPaquet", type="string", length=255)
*/
private $urlPaquet;
/**
* @Vich\UploadableField(mapping="paquet", fileNameProperty="urlPaquet")
* @var File
*/
private $paquetFile;
/**
* @ORM\Column(type="datetime")
*
* @var \DateTime
*/
private $updatedAt;
/**
* @param File|UploadedFile $unPaquetFile
*
* @return Paquet
*/
public function setPaquetFile(File $unPaquetFile = null)
{
$this->paquetFile = $unPaquetFile;
if ($unPaquetFile)
{
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
*
* @return Paquet
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* @return File|null
*/
public function getPaquetFile()
{
return $this->paquetFile;
}
/**
* @var string
*
* @ORM\Column(name="urlNotice", type="string", length=255,nullable=true)
*/
private $urlNotice;
/**
* @Vich\UploadableField(mapping="notice", fileNameProperty="urlNotice",nullable=true)
* @var File
*/
private $noticeFile;
/**
* @var string
*
* @ORM\Column(name="commentaire", type="text")
*/
private $commentaire;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set titre
*
* @param string $titre
*
* @return Paquet
*/
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* @return string
*/
public function getTitre()
{
return $this->titre;
}
/**
* Set urlPaquet
*
* @param string $urlPaquet
*
* @return Paquet
*/
public function setUrlPaquet($urlPaquet)
{
$this->urlPaquet = $urlPaquet;
return $this;
}
/**
* Get urlPaquet
*
* @return string|null
*/
public function getUrlPaquet()
{
return $this->urlPaquet;
}
/**
* @return File|null
*/
public function getNoticeFile()
{
return $this->noticeFile;
}
/**
* @param File|UploadedFile $uneNoticeFile
*
* @return Paquet
*/
public function setNoticeFile(File $uneNoticeFile = null)
{
$this->noticeFile = $uneNoticeFile;
if ($uneNoticeFile)
{
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* Set urlNotice
*
* @param string $urlNotice
*
* @return Paquet
*/
public function setUrlNotice($urlNotice)
{
$this->urlNotice = $urlNotice;
return $this;
}
/**
* Get urlNotice
*
* @return string
*/
public function getUrlNotice()
{
return $this->urlNotice;
}
/**
* Set commentaire
*
* @param string $commentaire
*
* @return Paquet
*/
public function setCommentaire($commentaire)
{
$this->commentaire = $commentaire;
return $this;
}
/**
* Get commentaire
*
* @return string
*/
public function getCommentaire()
{
return $this->commentaire;
}
}
就是这样,我想我已经把所有需要的信息都放了。我再说一遍,当我在本地(而不是在CAS中)进行身份验证时,我没有这个问题。而且我不必正常地序列化任何文件,对吧?
编辑:
在Paquet.php中,我实现了\ Serializable和两个方法:
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->noticeFile,
$this->paquetFile,
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->noticeFile,
$this->paquetFile,
) = unserialize($serialized, array('allowed_classes' => false));
}
同样的错误,我的方法很好吗?
编辑:好的,我刚刚在UserCas.php中添加了这两个函数:
public function serialize()
{
return serialize($this->id);
}
public function unserialize($serialized)
{
$this->id = unserialize($serialized);
}
答案 0 :(得分:0)
这是我的主要要求,它可能是正确的答案,也可能不是正确的答案。我所依靠的可能不是正确的答案。 对于我来说,使用您的代码在本地对其进行测试几乎是不可能的。
我假设在某些时候会发生以下情况:
$serlialization = serialize($userClass);
您的用户类对资源流(在本例中为File
)的引用。哪些无法序列化。
请参见here
资源不打算序列化并且不能持久化 通过会话变量跨页面加载。他们基本上只是 处理某些系统资源。 PHP将取消分配这些资源 在脚本执行结束时自动处理。
您将必须在将要序列化并具有资源引用的类上实现Serializable
接口。
让我们以Paquet
类为例。
class Paquet implements \Serializable
{
...
public function __clone()
{
// now it's a string reference not a resournce reference
$this->noticeFile = $this->noticeFile->getPath();
// Alternatively i think you can use base64_encode
$this->paquetFile = $this->paquetFile->getPath();
// the rest can stay the same
}
public function serialize()
{
return serialize(clone $this);
}
public function unserialize($serialized)
{
// TODO: Implement unserialize() method.
}
}
oyu当然不必使用克隆,还有其他方法。
class Paquet implements \Serializable
{
public function serialize()
{
$toSerialize = new \stdClass();
$toSerialize->noticeFile = $this->noticeFile->getPath();
$toSerialize->paquetFile = $this->paquetFile->getPath();
return serialize($toSerialize);
}
public function unserialize($serialized)
{
$unseriliazed = unserialize($serialized);
$this->noticeFile = new File($unseriliazed->noticeFile);
}
}
这应该处理资源流引用,因为它不再是引用,而是一个字符串。
请注意,您也可以使用__sleep()
,但也必须使用__wakeup
。它们或多或少与Serializable
接口做相同的事情,您只需要编写自己的实现即可。
再次,这是我的主要客座评价。如果不进行调整,它可能无法100%工作。
答案 1 :(得分:0)
检查一下,您的用户关系之一具有 File 属性,它本质上是不允许序列化的文件流。因此,您需要指定要序列化的内容:
https://symfony.com/doc/3.3/security/entity_provider.html
基本上你只需要序列化id、用户名和密码(三是上面页面的示例代码)