Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Die User-Entity mit Symfony Security

Die User-Entity mit Symfony Security

~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Bisher ist die GESAMTE API öffentlich – ab HIER beginnt der Security-Block: GENAU wie in der Symfony-Schulung (Kapitel 34) startet ALLES mit einer User-Entity, die UserInterface implementiert.

Die User-Entity generieren

docker compose exec php bin/console make:user

Der INTERAKTIVE Assistent fragt NACH dem Entity-Namen (User), dem eindeutigen Feld (email) und OB ein Passwort benötigt wird (ja) – GENAU dieselben Fragen wie in der Symfony-Schulung.

api/src/Entity/User.php
<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;

#[ApiResource(
    normalizationContext: ['groups' => ['user:read']],
    denormalizationContext: ['groups' => ['user:write']]
)]
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups(['user:read'])]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    #[Assert\NotBlank]
    #[Assert\Email]
    #[Groups(['user:read', 'user:write'])]
    private string $email = '';

    #[ORM\Column]
    private array $roles = [];

    #[ORM\Column]
    private string $password = '';

    #[Groups(['user:write'])]
    #[Assert\NotBlank]
    private ?string $plainPassword = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getUserIdentifier(): string
    {
        return $this->email;
    }

    public function getRoles(): array
    {
        $roles = $this->roles;
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

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

    public function setPassword(string $password): static
    {
        $this->password = $password;

        return $this;
    }

    public function getPlainPassword(): ?string
    {
        return $this->plainPassword;
    }

    public function setPlainPassword(?string $plainPassword): static
    {
        $this->plainPassword = $plainPassword;

        return $this;
    }

    public function eraseCredentials(): void
    {
        $this->plainPassword = null;
    }
}

Achtung: plainPassword gehört BEWUSST NUR zu user:write, NIEMALS zu user:read – das Klartext-Passwort darf UNTER KEINEN UMSTÄNDEN in einer API-Antwort erscheinen. password (das GEHASHTE Passwort) hat SOGAR GAR KEINE #[Groups]-Angabe und bleibt damit KOMPLETT unsichtbar.

Das Passwort hashen

GENAU wie in der Symfony-Schulung (Kapitel 35) übernimmt ein EventSubscriber oder State Processor (Kapitel 61-Konzept, HIER vorgezogen) das automatische Hashen von plainPassword zu password VOR dem Speichern – Details dazu in Kapitel 48.

Migration erstellen

docker compose exec php bin/console make:migration
docker compose exec php bin/console doctrine:migrations:migrate --no-interaction

Tipp: make:user passt AUTOMATISCH config/packages/security.yaml an (providers-Sektion mit App\Entity\User) – GENAU diese Datei wird in Kapitel 48 für JWT ERWEITERT.