Home > database >  Prevent storing entire user in Symfony session
Prevent storing entire user in Symfony session

Time:03-11

My app is using Symfony 5.4.2 and is relying on the new Authenticator Manager.

I recently encountered memory errors related to session_open(). When investigating I noticed the entire User entity is stored in the session file (as a PHP serialized string), starting with _sf2_attributes|.

So every related entity is stored there as well (every property, user purchases and their details, and so on), which can be pretty heavy (I have session files that are more than 100 Kb, which sounds enormous to me).

Is that expected to work this way?
Is there a way to prevent serializing all User entity properties?

CodePudding user response:

You must serialize your user entity class implementing \Serializable interface and defining serialize, unserialize and __sleep methods, defining which attributes will be included in the session. With __sleep you will grant that only specifics attributes enter into the serialization process; for example:

/**
 * Usuario
 *
 * @ORM\Table(name="usuario", uniqueConstraints={@ORM\UniqueConstraint(name="usuario_username_uniq", columns={"username"})}, indexes={@ORM\Index(name="usuario_rol_idx", columns={"rol_id"}), @ORM\Index(name="usuario_nodo_idx", columns={"estructura_organizativa_id"}), @ORM\Index(name="usuario_id_publico_idx", columns={"id_publico"})})
 * @ORM\Entity(repositoryClass="App\Repository\UsuarioRepository")
 * @UniqueEntity("username", message="Ya existe un usuario con ese identificador")    
 */
class Usuario implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface, \Serializable
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\SequenceGenerator(sequenceName="usuario_id_id_seq", initialValue=1, allocationSize=100)   
     */
    private $id;

    /**
     * @var string
     * 
     * @Assert\NotBlank(message="El identificador es obligatorio.")
     * @ORM\Column(name="username", type="string", unique=true, length=100, nullable=false)
     */
    private $username;

    /**
     * @var string
     * 
     * @Assert\NotBlank(message="El nombre es obligatorio.")
     * @ORM\Column(name="nombre_completo", type="string", length=255, nullable=false)
     */
    private $nombreCompleto;

    /**
     * @var string
     * 
     * @Assert\Length(
     *      min=6, 
     *      max=255,
     *      minMessage="La contraseña debe tener como mínimo {{ limit }} caracteres",
     *      maxMessage="La contraseña no puede exceder los {{ limit }} caracteres."
     * )
     * @ORM\Column(name="password", type="string", length=255, nullable=false)
     */
    private $password;

    /**
     * @var boolean
     *
     * @ORM\Column(name="activo", type="boolean", nullable=false)
     */
    private $activo;

        
    /**
     * @var type \App\Entity\Rol
     * 
     * @ORM\ManyToOne(targetEntity="Rol", fetch="EAGER")     
     * @ORM\JoinColumn(name="rol_id", referencedColumnName="id", nullable=false, onDelete="RESTRICT")
     */
    private $rol;

    

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->activo = true;           
    }

    /**
     * Set username
     *
     * @param string $username
     * @return Usuario
     */
    public function setUsername(?string $username): self
    {
        $this->username = $username;

        return $this;
    }

    /**
     * Get username
     *
     * @return string 
     */
    public function getUsername(): string
    {
        return $this->username;
    }

    /**
     * Set nombreCompleto
     *
     * @param string $nombreCompleto
     * @return Usuario
     */
    public function setNombreCompleto(string $nombreCompleto): self
    {
        $this->nombreCompleto = $nombreCompleto;

        return $this;
    }

    /**
     * Get nombreCompleto
     *
     * @return string 
     */
    public function getNombreCompleto(): string
    {
        return $this->nombreCompleto;
    }

    /**
     * 
     * @return array
     */
    public function getRoles(): array
    {
        $coleccion = new ArrayCollection();
        $coleccion->add($this->getRol()->getToken());
        $coleccion[] = "ROLE_USER";

        return array_unique($coleccion->toArray());
    }

    /**
     * Set password
     *
     * @param string $password
     * @return Usuario
     */
    public function setPassword(?string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Get password
     *
     * @return string 
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    /**
     * Set activo
     *
     * @param bool $activo
     * @return Usuario
     */
    public function setActivo(bool $activo): self
    {
        $this->activo = $activo;

        return $this;
    }

    /**
     * Get activo
     *
     * @return bool 
     */
    public function getActivo(): bool
    {
        return $this->activo;
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId(): ?int
    {
        return $this->id;
    }

    

    /**
     * 
     * @return void
     */
    public function eraseCredentials(): void
    {
        
    }

    /**
     * 
     * @return string
     */
    public function getSalt(): string
    {
        return '';
    }

    /**
     * 
     * @return bool
     */
    public function isAccountNonExpired(): bool
    {
        return true;
    }

    /**
     * 
     * @return bool
     */
    public function isAccountNonLocked(): bool
    {
        return true;
    }

    /**
     * 
     * @return bool
     */
    public function isCredentialsNonExpired(): bool
    {
        return true;
    }

    /**
     * 
     * @return bool
     */
    public function isEnabled(): bool
    {
        return $this->activo;
    }

    /**
     * 
     * @return string
     */
    public function serialize(): string
    {
        return serialize([
            $this->username,
            $this->nombreCompleto,
            $this->password,
            $this->activo,
            $this->id,
            $this->idPublico
        ]);
    }

    /**
     * 
     * @param type $serialized
     * @return void
     */
    public function unserialize($serialized): void
    {
        list(
                $this->username,
                $this->nombreCompleto,
                $this->password,
                $this->activo,
                $this->id,
                $this->idPublico
                ) = unserialize($serialized);
    }

    /**
     * 
     * @return array
     */
    public function __sleep(): array
    {
        return ['username', 'nombreCompleto', 'password', 'activo', 'id', 'idPublico'];
    }

    /**
     * 
     * @return string
     */
    public function __toString(): string
    {
        return $this->nombreCompleto;
    }    
    

    /**
     * 
     * @param UserInterface $user
     * @return bool
     */
    public function isEqualTo(UserInterface $user): bool
    {
        if ($this->getUsername() === $user->getUsername() && $this->getPassword() === $user->getPassword() && $this->getSalt() === $user->getSalt()) {
            return true;
        }

        return false;
    }

    /**
     * @return string
     * * */
    public function getUserIdentifier(): string
    {
        return (string) $this->username;
    }

}
  • Related