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

Login-Formular implementieren

Login-Formular implementieren

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

Mit dem Grundgerüst aus Kapitel 26 bauen wir jetzt den tatsächlichen Login-Authenticator – Symfonys eingebauten form_login-Mechanismus.

Den Login-Authenticator generieren

php bin/console make:security:form-login

Fragt nach dem Controller-Namen, den Routennamen für Login/Logout und ob nach erfolgreichem Login zu einer festen Seite weitergeleitet werden soll. Erzeugt einen Controller UND passt security.yaml automatisch an.

Der generierte Login-Controller

src/Controller/SecurityController.php
<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class SecurityController extends AbstractController
{
    #[Route('/login', name: 'app_login')]
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        $fehler = $authenticationUtils->getLastAuthenticationError();
        $letzteEmail = $authenticationUtils->getLastUsername();

        return $this->render('security/login.html.twig', [
            'last_username' => $letzteEmail,
            'error' => $fehler,
        ]);
    }

    #[Route('/logout', name: 'app_logout')]
    public function logout(): void
    {
        throw new \LogicException('Wird automatisch vom Security-System abgefangen.');
    }
}

logout() wird NIEMALS tatsächlich ausgeführt – Symfonys Security-System fängt die Anfrage an /logout bereits VOR dem Controller ab (konfiguriert in security.yaml). Die Exception dient als klares Signal: "landet dieser Code jemals in der Ausführung, ist etwas falsch konfiguriert".

Das Login-Template

templates/security/login.html.twig
{% extends 'base.html.twig' %}

{% block body %}
    <h1>Anmelden</h1>

    {% if error %}
        <div class="alert alert-error">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    <form method="post">
        <label for="email">E-Mail</label>
        <input type="email" id="email" name="email" value="{{ last_username }}" required autofocus>

        <label for="password">Passwort</label>
        <input type="password" id="password" name="password" required>

        <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">

        <button type="submit">Anmelden</button>
    </form>
{% endblock %}

WICHTIG: Dieses Formular ist KEIN Symfony Form (Kapitel 15) – form_login erwartet ein KLASSISCHES HTML-Formular mit den Feld-name-Attributen email/password, direkt von der Firewall verarbeitet, NICHT vom Controller. Der CSRF-Token (Kapitel 18) wird deshalb HÄNDISCH per csrf_token('authenticate') eingefügt.

security.yaml: form_login konfigurieren

config/packages/security.yaml
security:
    # ... providers wie in Kapitel 26 ...

    firewalls:
        main:
            lazy: true
            provider: app_user_provider
            form_login:
                login_path: app_login
                check_path: app_login
                enable_csrf: true
            logout:
                path: app_logout
                target: app_login

login_path zeigt das Formular AN, check_path verarbeitet den POST-Request – meist DIESELBE Route (beide GET UND POST auf /login, GENAU wie das Formular-Muster aus Kapitel 9). enable_csrf: true aktiviert die Prüfung des _csrf_token-Felds von oben.

<nav>
    <a href="{{ path('project_index') }}">Projekte</a>

    {% if app.user %}
        <span>Angemeldet als {{ app.user.name }}</span>
        <a href="{{ path('app_logout') }}">Abmelden</a>
    {% else %}
        <a href="{{ path('app_login') }}">Anmelden</a>
    {% endif %}
</nav>

app.user (aus Kapitel 13 bereits bekannt) ist JETZT tatsächlich befüllt – null für nicht eingeloggte Besucher, sonst die User-Entity des eingeloggten Nutzers.

Tipp: Ein Login-Versuch schlägt aktuell IMMER fehl, da noch kein Nutzer mit korrekt gehashtem Passwort existiert – Kapitel 28 baut die Registrierung, die das behebt.