CodeGym /Kurse /Spring Security /Benutzername oder E-Mail: Login-Identifikator

Benutzername oder E-Mail: Login-Identifikator

Spring Security
Level 15, Lektion 4
Verfügbar

1. Login-Identifikator: Eine Regel

An dieser Stelle haben wir bereits fast alle Details des DB-backed Auth gesammelt: UserAccount aus der DB, CustomUserDetailsService, Mapping in UserDetails, DaoAuthenticationProvider und das Verständnis, dass Rollen rechtzeitig in den Auth-Pfad gelangen müssen. Es bleibt eine letzte Variable, aufgrund derer die Kette immer noch auseinanderfallen kann: welche Zeichenfolge als Login-Identifikator betrachtet wird und welcher Benutzername später im SecurityContext landet.

Wenn Sie zum ersten Mal die Methode loadUserByUsername sehen, scheint die Frage geklärt: „Wir loggen uns mit dem Benutzernamen ein, sonst ist Spring Security unglücklich.“ In Wahrheit wird Spring Security nicht durch die Wahl von email unglücklich, sondern durch Inkonsequenz. Diese Methode ist lediglich ein Vertrag: Sie bekommt eine Login-Zeichenfolge und muss entweder UserDetails zurückgeben oder ehrlich sagen: „nicht gefunden“. Wenn Sie heute nach username suchen, morgen nach email, und UserDetails#getUsername() gibt etwas ganz Drittes zurück, dann erhalten Sie eine Authentifizierung vom Niveau „Errate die Melodie“.

Der Schlüsselgedanke: Der Login-Identifikator ist eine architektonische Entscheidung, nicht „wie es im Tutorial stand“. In der Secure Content Platform API haben wir sowohl username als auch email (beide eindeutig), und beide sind gute Kandidaten. Aber der Kandidat muss zur einzigen Regel werden: im Repository, im CustomUserDetailsService, in Logs und in dem, was Sie als authentication.getName() sehen.

Um das auf Codeebene (und auf mentaler Ebene) zu verankern, ist es sinnvoll, die Variable nicht username, sondern login zu nennen, damit man nicht durch den Methodennamen hypnotisiert wird:

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserDetailsService {
    // Wichtig: Der Parameter ist die "Login-Zeichenfolge" (login) und nicht unbedingt der Benutzername.
    // Welche Bedeutung diese Zeichenfolge hat, entscheiden Sie (und halten diese Regel dann überall gleich).
    UserDetails loadUserByUsername(String login);
}

Ja, das Interface ist nicht so deklariert (dort steht String username), aber sich vorzustellen „das ist login“ ist hilfreich. Spring Security wird nicht böse.

2. username vs email auf dem Backend

Die Wahl zwischen username und email wird oft zu einer Debatte auf dem Niveau „vim vs. intellij“. Aber wir brauchen eine ingenieurwissenschaftliche Entscheidung: Was ist einfacher zu warten, was ist stabiler, welche Fehler werden wir in einem Monat erwischen, wenn wir vergessen haben, was hier eigentlich passiert – auf dem Backend ist der Login-Identifikator nicht nur UX, sondern auch eindeutige Indizes, Zeichennormalisierung, Logging, Umbenennung von Benutzern und sogar (heute nicht, aber generell) die Arbeit des Admin-Panels.

Im Folgenden vergleichen wir diese beiden Optionen nicht danach „was für den Benutzer bequemer ist“, sondern „was in Code und Daten besser hält“. Und noch ein wichtiger Punkt: Beide Optionen können korrekt sein. Inkorrekt ist meist die dritte Option „es hat sich irgendwie so ergeben“.

Fassen wir die wichtigsten Unterschiede in einer kompakten Tabelle zusammen (nicht als „Vor- und Nachteile auf einer Serviette“, sondern als Checkliste für eine architektonische Entscheidung):

Kriterium username als Login email als Login
Stabilität des Identifikators Normalerweise ändert er sich selten (oft nie) Kann sich ändern (Benutzer wechselt die E-Mail)
Normalisierung (Groß-/Kleinschreibung/Leerzeichen) Abhängig von den Projektregeln Fast immer nötig: trim + lowercase (und Einigung, was das bedeutet)
Privatsphäre In Logs und Antworten weniger PII E-Mail ist PII und „leuchtet“ häufiger in Logs/Fehlern auf
Eindeutigkeit Username muss eindeutig sein E-Mail muss eindeutig sein (und das ist meist erwartet)
UX Benutzer muss sich den Nick merken Benutzer merkt sich die E-Mail meist besser
Komplexität von Migrationen Normalerweise einfacher E-Mail-Wechsel kann den „Benutzernamen“ im SecurityContext betreffen

Im Folgenden gehen wir etwas detaillierter darauf ein – genau an den Stellen, die meist „schießen“, wenn das Projekt bereits lebt.

Stabilität und Lebenszyklus des Identifikators

Am Anfang scheint es, dass der Login-Identifikator einfach ein Feld ist, nach dem wir findBy... machen. Aber nach einiger Zeit beginnt das Projekt ein echtes Leben zu führen: Jemand ändert seine E-Mail, jemand möchte seinen Usernamen umbenennen, jemand bekommt Support vom Admin „Übertragen Sie mir mein Konto auf die neue E-Mail“. Und dann stellt sich plötzlich heraus, dass der gewählte Identifikator nicht nur die Suche in der DB ist, sondern auch die Art und Weise, wie das System den Benutzer intern im SecurityContext „nennt“.

username wird normalerweise als interner Kontoname wahrgenommen, den man stabil halten kann. Selbst wenn der Benutzer seine E-Mail ändert, bleibt sein username gleich – und Ihre Logs, Audits und internen Verknüpfungen „übermalen“ sich nicht. Das ist ein großer Vorteil für die Wartbarkeit: Es gibt weniger Gründe, etwas in der Security-Schicht zu ändern.

email als Login ist oft bequemer für den Benutzer, aber von Natur aus veränderlicher. Wenn Sie also die E-Mail als UserDetails#getUsername() verwenden, taucht in Logs, in authentication.getName() und an allen „verbundenen Stellen“ eine neue Zeichenfolge auf. Manchmal ist das in Ordnung, manchmal überraschend ärgerlich.

Im Lehrprojekt ist es besonders wichtig, dass das Modell vorhersagbar ist und nicht 10 Erklärungen erfordert, warum gestern in den Logs alice@mail.com stand und heute alice@newmail.com – ist das sicher derselbe Benutzer?

Eindeutigkeit und Normalisierung

Wenn eine Login-Zeichenfolge ins System gelangt, kommt sie nicht aus einer idealen Welt. Leute kopieren E-Mails mit einem Leerzeichen am Ende, geben ALICE@MAIL.COM ein oder fügen manchmal „schöne Anführungszeichen“ ein (ja, das gibt es, und ja, das ist schmerzhaft). Daher erfordert der Login-Identifikator fast immer eine Normalisierung, sonst erhalten Sie „Benutzer existiert definitiv in der DB, kann sich aber nicht anmelden“.

Für username sind die Normalisierungsregeln Ihre Entscheidung. Sie können username case-sensitive oder case-insensitive machen, Leerzeichen verbieten, Unterstriche erlauben. Hauptsache, die Regel ist einheitlich.

Für email ist eine Normalisierung nahezu unvermeidlich. Mindestens trim und normalerweise toLowerCase(). Und hier ist wichtig: Die Normalisierung muss konsistent sein. Sie dürfen nicht einmal die E-Mail speichern, wie sie ist, und beim Login in Kleinbuchstaben suchen, denn dann fangen Sie „nicht gefunden“ auf glattem Parkett.

Eine kleine Hilfsfunktion, die oft zur Rettung wird (und gleichzeitig daran erinnert, dass eine Zeichenfolge nicht einfach eine Zeichenfolge ist):

private String normalizeEmail(String raw) {
    // Normalisierung ist Teil des "Login-Vertrags", nicht nur Kosmetik.
    if (raw == null) return null;

    // Minimaler Satz: trim + lowercase (später können strengere Regeln hinzugefügt werden).
    return raw.trim().toLowerCase(); // " Alice@Mail.com " -> "alice@mail.com"
}

Ja, das sieht zu einfach aus. Aber genau solche „zu einfachen“ Dinge brechen den Login auf der Demo im feierlichsten Moment.

Privatsphäre und Fehlermeldungen

Wenn Sie email als Login-Identifikator wählen, erhöhen Sie automatisch das Risiko, dass die E-Mail überall „aufscheint“: in Meldungen von UsernameNotFoundException, in Logs, in Fehlerverfolgungen. Innerhalb des Teams ist das nicht schlimm, aber generell ist E-Mail PII, und man sollte vorsichtig damit umgehen.

Hier gibt es einen feinen Punkt: Selbst wenn wir jetzt keinen REST-freundlichen JSON-Fehler bauen, Gewohnheiten werden schon heute geformt. Wenn Sie überall "User not found: " + login schreiben und login ist eine E-Mail, dann erzeugen Sie sich Logs, die Sie später bereinigen müssen.

Für username ist dieses Risiko in der Regel geringer, da der Username oft keine personenbezogenen Daten sind (obwohl es auch anders sein kann). Deshalb erweist sich im Lehrprojekt, in dem wir Klarheit und möglichst wenige Ablenkungen wünschen, username oft als die „weniger toxische“ Option.

Das bedeutet nicht „E-Mail ist verboten“. Es bedeutet: Wenn Sie sich für E-Mail entscheiden, übernehmen Sie die Disziplin – Normalisierung, sorgfältige Fehlermeldungen, sorgfältiges Logging.

3. Entscheidung für die Secure Content Platform API

In dieser Phase brauchen wir nicht „die ideale Lösung für alle Produkte der Welt“, sondern eine Lösung, die das Lehrprojekt stabil, verständlich und leicht überprüfbar macht. In der Secure Content Platform API sind für uns Rollen, Kontostatus, korrektes Laden von Rollen aus der DB und vorhersagbares Verhalten des SecurityContext wichtig. Daher ist es sinnvoll festzulegen: In diesem Hauptzweig des Projekts erfolgt der Login per username.

Diese Entscheidung passt gut zu unseren Zielen: Der Username ist normalerweise stabiler, erfordert weniger Normalisierung und ähnelt weniger personenbezogenen Daten. Die E-Mail bleibt dabei im Modell UserAccount als wichtiges Profil-/Kontaktfeld, aber nicht als Schlüssel „wer du innerhalb der Security bist“. So verlieren wir die E-Mail nicht, machen sie aber nicht zum Zentrum der Auth-Mechanik.

Wichtiger als die Wahl selbst ist die Konsistenz. Hier ist eine kleine „Konsistenzmatrix“, die man im Kopf behalten sollte (und die man bei einem Code-Review gerne mit den Augen überprüft):

Element Muss sich stützen auf… (wenn Login = username)
UserAccountRepository Suche nach username
CustomUserDetailsService findByUsername(...)
UserDetails#getUsername() gibt account.getUsername() zurück
Logs/Fehler verwenden den Username (oder neutrale Formulierungen)

Wenn diese vier Punkte übereinstimmen, wird Ihr Auth-Flow „langweilig“. Und ein langweiliger Auth-Flow ist ein Kompliment.

4. Code für Login per username

Wenn die Entscheidung gefallen ist, sieht der Code fast „verdächtig einfach“ aus. Und das ist gut: Wir wollen nicht, dass die Authentifizierung von 15 Bedingungen und 3 Verzweigungen „aber was, wenn das eine E-Mail ist“ abhängt. Wir brauchen genau eines: die Login-Zeichenfolge nehmen, das Konto finden, UserDetails zusammenbauen. Alles andere (Passwortprüfung, Prüfung von enabled/locked, Erstellung von Authentication) erledigt Spring Security bereits über DaoAuthenticationProvider.

Im Folgenden die wesentlichen Code-Stücke. Nicht als „einfügen und beten“, sondern als Orientierung: Wo genau im Projekt lebt die Entscheidung „wir loggen uns per Username ein“.

Repository für Auth und Rollen

Für den Hauptzweig des Projekts legen wir ein Auth-Repository fest: Suche nach username mit explizitem Laden der Rollen. fetch join, @Transactional und andere Techniken sind genau so nützlich, wie sie zu derselben Garantie führen. Aber der Snapshot des Projekts bleibt einfach und eindeutig: Zum Zeitpunkt von toUserDetails(...) müssen die Rollen bereits verfügbar sein.

Im Auth-Szenario werden die Rollen sofort benötigt, weil Spring Security nach erfolgreicher Authentifizierung die Authorities in das Authentication legt. Wenn die Rollen lazy sind und nicht geladen wurden, erhalten Sie entweder einen Fehler oder einen „Benutzer ohne Rollen“, was fast immer gleichbedeutend mit „Benutzer ohne Berechtigungen“ ist.

Deshalb sollte das Repository für Auth so gemacht werden, dass es das Konto sofort mit Rollen zurückgibt. Der für den Kurs sanfteste Weg ist @EntityGraph, um nicht 40 Minuten in JPA-Exkurse abzutauchen:

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {

    // Dies ist das Haupt-Auth-Repository des Projekts: Für den Login müssen die Rollen sofort geladen werden.
    @EntityGraph(attributePaths = "roles")
    Optional<UserAccount> findByUsername(String username);
}

Beachten Sie: Der Methodenname lautet findByUsername. Wir müssen nicht WithRoles in den Namen schreiben, wenn wir das Laden der Rollen explizit durch die Annotation sicherstellen. Wichtig ist etwas anderes: Im Auth-Pfad muss diese Methode die primäre sein.

Das ist die Repository-Basislinie, auf die sich dann CustomUserDetailsService stützt: ein Login-Identifikator, ein User Store, Rollen kommen sofort mit.

CustomUserDetailsService und getUsername()

Nun der wichtigste Punkt des Tages: CustomUserDetailsService. Es ist keine Magie, aber es ist wie ein „Übersetzer an der Grenze“: Es übersetzt Ihr UserAccount in die Sprache von Spring Security. Und genau hier müssen wir ehrlich sein: Wenn der Login der Username ist, dann suchen wir nach dem Username und geben den Username als getUsername() zurück.

Die zentrale Methode (minimal und lesbar):

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

@Override
public UserDetails loadUserByUsername(String login) {
    // login ist hier der Benutzername, weil wir das auf Projektebene so entschieden haben.
    UserAccount account = repository.findByUsername(login)
            // In einem realen Projekt könnten der Ausnahmetext und die Protokollierung neutraler sein.
            .orElseThrow(() -> new UsernameNotFoundException("User not found: " + login));

    // Wandeln Sie das Domänenmodell in UserDetails um (Sprache von Spring Security).
    return toUserDetails(account);
}

Und hier der Ort, an dem die „Login-Zeichenfolge“ zum „Benutzernamen im Security-Modell“ wird:

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

private UserDetails toUserDetails(UserAccount a) {
    return User.withUsername(a.getUsername()) // principal name: genau der Benutzername
            .password(a.getPasswordHash())     // in UserDetails legen wir den Hash, nicht das rohe Passwort
            .disabled(!a.isEnabled())          // Flag "darf einloggen"
            .accountLocked(!a.isAccountNonLocked()) // Flag für Konto-Sperrung
            .authorities(roleNames(a))         // Rollen/Berechtigungen müssen aus der DB kommen
            .build();
}

Beachten Sie den psychologischen Effekt: Wenn Sie sich für username entschieden haben, wirkt withUsername(a.getUsername()) fast wie eine Tautologie. Und das ist wieder ein gutes Zeichen – das System widerspricht sich nicht selbst.

5. Variante mit email-Login

Manchmal möchte man aus Produktsicht genau den E-Mail-Login, und das ist in Ordnung. Aber es ist wichtig zu verstehen: E-Mail-Login ist nicht nur „eine andere Spalte in der Abfrage“. Es ist die Disziplin der Normalisierung, die Auswirkung auf den SecurityContext, die Frage „wie wird der Principal in Logs und im Code von Controllern/Services aussehen, die authentication.getName() verwenden“.

Technisch gesehen ist das eine funktionierende Alternative, aber nicht der Hauptsnapshot des Projekts. Im Folgenden eine alternative Variante, um zu zeigen: Es umzusetzen ist nicht schwer, aber es muss konsequent gemacht werden. Hier ist uns nur der Auth-Pfad selbst wichtig: Wie die Login-Zeichenfolge in der DB gesucht wird und welcher Name in die UserDetails gelangt.

Das Repository ändert sich fast unmerklich:

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {

    // Die Bedeutung ändert sich: Nun meinen wir mit "login" die E-Mail.
    @EntityGraph(attributePaths = "roles")
    Optional<UserAccount> findByEmail(String email);
}

CustomUserDetailsService beginnt explizit, die Login-Zeichenfolge zu normalisieren und nach E-Mail zu suchen:

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

@Override
public UserDetails loadUserByUsername(String login) {
    // Normalisierung ist erforderlich, sonst kann dieselbe E-Mail "nicht gefunden werden".
    String email = normalizeEmail(login);

    UserAccount account = repository.findByEmail(email)
            .orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));

    return toUserDetails(account);
}

Und das Wichtigste: UserDetails#getUsername() (also withUsername(...)) wird ebenfalls zur E-Mail, sonst erhalten Sie einen internen Widerspruch:

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

private UserDetails toUserDetails(UserAccount a) {
    return User.withUsername(a.getEmail()) // principal name: jetzt ist es die E-Mail
            .password(a.getPasswordHash())
            .authorities(roleNames(a))
            .build();
}

Als Ergebnis wird authentication.getName() die E-Mail zur&uumlckgeben. Das kann in Ordnung sein, man muss es nur als Konsequenz der Entscheidung akzeptieren und nicht „oh, warum ist plötzlich die E-Mail überall aufgetaucht“.

Doppelter Login: username und email

Bei Anfängern taucht fast immer die Versuchung auf: „Warum suchen wir nicht sowohl nach Username als auch nach E-Mail – das ist doch für alle bequem.“ In der Praxis wird daraus eine Login-Lotterie, weil Sie Mehrdeutigkeit einführen. Was, wenn ein Username wie eine E-Mail aussieht? Was, wenn Sie alte Benutzerimporte haben, bei denen Username = E-Mail? Was, wenn verschiedene Benutzer potenziell mit Zeichenfolgen kollidieren können? Und selbst wenn sie nicht kollidieren, bekommen Sie ein Wartungsproblem: Niemand kann schnell beantworten „wonach loggen wir uns ein?“.

Technisch sieht das wie eine „bequeme“ Repository-Methode aus:

import java.util.Optional;

public interface UserAccountRepository {
    // Dies fügt Mehrdeutigkeit hinzu: Das System selbst versteht nicht mehr, was die Identität ist.
    Optional<UserAccount> findByUsernameOrEmail(String username, String email);
}

Aber auf Modellebene bedeutet das: „Das System weiß nicht, was die Identität des Benutzers ist.“ Und Spring Security liebt es, wenn die Identität klar ist: es gibt einen Principal (wer bist du), Credentials (womit weist du es nach) und Authorities (was darfst du). Wenn Sie den Login verschwimmen lassen, verschwimmen Sie auch den Principal.

Wenn Sie beide Varianten wirklich unterstützen müssen, geschieht das nicht mit „OR in der Abfrage“, sondern mit einer separaten, sehr expliziten Regel. Zum Beispiel: „Wenn die Zeichenfolge @ enthält, betrachten wir sie als E-Mail, sonst als Username.“ Aber das ist bereits ein separates Design von Regeln, und in einem Grundlagenkurs bringt es meist mehr Lärm als Nutzen.

6. DB-backed Auth Flow bis zum SecurityContext

Nach allen Entscheidungen sieht die Arbeitskette des Projekts so aus: UserAccountRepository (username + roles) → CustomUserDetailsService → UserDetails → DaoAuthenticationProvider + PasswordEncoder → Authentication → SecurityContext. In diesem Moment wird die DB zum wichtigsten User Store der Anwendung, und die alten In-Memory-Benutzer können nur noch als ausdrücklich vereinbartes Testwerkzeug übrig bleiben, aber nicht als parallele Realität.

Wenn Sie bereits einen Service, ein Mapping und einen Provider haben, möchte man sagen: „Das war’s, es passiert irgendwo da drinnen in Spring Security von selbst.“ Aber jetzt ist der wichtige Moment: Genau hier haben Sie die Chance, den Login nicht mehr als Magie zu betrachten. Der vollständige Auth Flow ist eine Kette von ganz konkreten Schritten: Ein Filter nimmt die Anfrage entgegen, der Manager delegiert an den Provider, der Provider lädt den Benutzer, prüft Passwort und Account-Flags und legt das Ergebnis dann in den SecurityContext. Und der gewählte Login-Identifikator ist einfach eine Zeichenfolge, die diesen gesamten Weg durchläuft.

Hier ist ein Schema (vereinfacht, aber auf den Punkt) im Format eines Sequenzdiagramms:

sequenceDiagram
    participant C as Client
    participant F as Auth Filter
    participant M as AuthenticationManager
    participant P as DaoAuthenticationProvider
    participant U as CustomUserDetailsService
    participant R as UserAccountRepository
    participant E as PasswordEncoder
    participant S as SecurityContext

    C->>F: "credentials (login + password)"
    F->>M: "authenticate(token)"
    M->>P: "authenticate(token)"
    P->>U: "loadUserByUsername(login)"
    U->>R: "findBy...(login)"
    R-->>U: "UserAccount (+roles)"
    U-->>P: "UserDetails"
    P->>E: "matches(raw, hash)"
    E-->>P: "true/false"
    P-->>M: "authenticated token (authorities)"
    M-->>F: "Authentication"
    F->>S: "setAuthentication(...)"

Beachten Sie eine einfache Sache: Die Login-Zeichenfolge gelangt in loadUserByUsername(login). Das heißt, die Wahl von username oder email ist die Wahl, welche Bedeutung der Parameter login hat und was Sie letztendlich in UserDetails.withUsername(...) schreiben.

Und jetzt ein kleiner angewandter Teil, um die Konsequenzen der Wahl zu sehen, ohne Filter zu debuggen. In unserem Projekt gibt es den Endpoint /api/me (persönlicher Bereich). Man kann ihn während der Entwicklung so einfach wie möglich halten: Er gibt zurück, was Spring Security gerade als Ihren Benutzernamen betrachtet.

import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MeController {

    @GetMapping("/api/me")
    public String me(Authentication authentication) {
        // getName() ist der "Name" des Principals, den Sie über withUsername(...) gesetzt haben.
        return "You are: " + authentication.getName(); // You are: alice
    }
}

Wenn Sie sich per username anmelden, sehen Sie alice. Wenn per email, sehen Sie alice@mail.com. Und das ist keine „Anzeigekleinigkeit“: Es ist Ihr Principal-Name, der in Logs, in der Diagnose und überall dort auftaucht, wo die Anwendung fragt „Wer macht die Anfrage?“.

7. Typische Fehler bei der Wahl des Login-Identifikators

Fehler Nr. 1: Das Projekt hat „entschieden, sich per E-Mail anzumelden“, sucht aber nach Username (oder umgekehrt).
Das ist ein Klassiker: In der DB hat der Benutzer eine E-Mail, Sie geben die E-Mail im Login ein, aber im CustomUserDetailsService steht immer noch findByUsername(login). Infolgedessen erhalten Sie eine UsernameNotFoundException, obwohl der Benutzer „definitiv existiert“. Das heilt nicht durch Magie, sondern durch genau die Konsistenzregel: Repository, Service und withUsername(...) müssen dasselbe sagen.

Fehler Nr. 2: loadUserByUsername sucht nach einem Feld, aber UserDetails#getUsername() gibt ein anderes zurück.
Zum Beispiel suchen Sie per E-Mail (weil es bequemer ist), machen aber in toUserDetails() ein withUsername(account.getUsername()). Innerhalb funktioniert es vielleicht sogar „einloggen“, aber dann erhalten Sie überraschende Effekte: authentication.getName() stimmt nicht mit der Login-Zeichenfolge überein, in Logs steht etwas anderes, in der Datenbank wieder etwas anderes, und Sie selbst können nicht erklären, was der „Benutzername“ im System ist.

Fehler Nr. 3: E-Mail-Login ohne Normalisierung.
Wenn Sie kein trim und lowercase machen, wird der Benutzer, der Alice@Mail.com eingegeben hat, aus Sicht der DB-Suche zu einer „anderen Person“. Manchmal zeigt sich das nur bei einem von hundert Benutzern – und genau deshalb ist es unangenehm, das zu erwischen. Normalisierung ist keine Verzierung, sondern Teil des Login-Vertrags.

Fehler Nr. 4: Der Versuch „beide zu unterstützen“ durch OR in der Abfrage ohne Regeln.
Solange es wenige Benutzer gibt, scheint das bequem. Später fangen Sie sich Mehrdeutigkeit ein und bekommen Angst vor Ihren eigenen Daten. Spring Security liebt eine klare Identität des Principals; OR ohne Policy macht die Identität schwimmend. Wenn Sie beide unterstützen wollen, dann nur nach einer expliziten, dokumentierten Logik, nicht nach „die Datenbank soll entscheiden“.

Fehler Nr. 5: Rollen wurden im Auth-Szenario nicht geladen, und der Benutzer wird „ohne Berechtigungen“ authentifiziert.
Das wirkt besonders täuschend: Das Passwort stimmt, die Anmeldung ist erfolgreich, aber die Zugriffe funktionieren nicht. Der Grund: In UserDetails gibt es keine Authorities, weil die roles nicht aus der DB kamen. Das heilt man, indem die Auth-Repository-Methode den Benutzer zwingend mit Rollen zurückgibt (via @EntityGraph oder einem ähnlichen Ansatz), und leere Rollen entweder verboten oder als separater bewusster Szenario behandelt werden.

1
Aufgabe
Spring Security, Level 15, Lektion 4
Gesperrt
Username als einziger Login-Identifier
Username als einziger Login-Identifier
1
Aufgabe
Spring Security, Level 15, Lektion 4
Gesperrt
E-Mail als einziger Login-Identifikator mit Normalisierung
E-Mail als einziger Login-Identifikator mit Normalisierung
1
Umfrage/Quiz
DB-Authentifizierung, Level 15, Lektion 4
Nicht verfügbar
DB-Authentifizierung
Benutzer, Rollen, Autorisierung
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION