CodeGym /Kurse /Spring Security /PasswordEncoder und DelegatingPasswordEncoder

PasswordEncoder und DelegatingPasswordEncoder

Spring Security
Level 6, Lektion 2
Verfügbar

1. Raw- und Stored-Passwort in der Anwendung

Wir haben bereits gesehen, dass ein Passwort nicht im Klartext (raw) gespeichert werden darf und dass kein umkehrbares Verschlüsselungsmodell, sondern eine rechenintensive Passwortverifikation benötigt wird. Nun ist es wichtig, dies in korrekten Spring-Code zu gießen: wo das Raw-Passwort lebt, was genau gespeichert wird und wer überhaupt die Überprüfung vornimmt.

Solange Sie nur Lern-Controller und -Services schreiben, neigt man leicht dazu, das Passwort wie ein weiteres Feld im DTO zu behandeln: „Na ja, ein String eben“. Das Problem ist, dass ein Passwort ein Geheimnis ist und einen anderen Lebenszyklus hat. Wenn Sie gedanklich nicht zwischen „das, was der Benutzer eingegeben hat“ und „das, was wir speichern“ trennen, schreiben Sie mit ziemlicher Sicherheit Code, der entweder unsicher ist oder einfach nicht wie erwartet funktioniert (und manchmal beides gleichzeitig).

Lassen Sie uns zwei Begriffe vereinbaren, die wir im gesamten Kurs verwenden werden (und sehr empfehlenswert auch in Ihrem Code):

raw password – das ist das, was der Benutzer eingibt (z. B. im JSON des Request-Bodys, im Formular, bei Basic Auth). Dieser Wert sollte nur sehr kurz leben: ankommen, überprüft werden, verschwinden. Er darf nicht in die Datenbank, in Logs, in Exceptions oder in „temporäre Variablen für alle Fälle“ gelangen.

stored password (oder password hash, password encoded value) – das ist der String, den die Anwendung als „Passwort-Repräsentation“ speichert. Dieser String ist nicht gleich dem ursprünglichen Passwort und muss nicht einmal für dasselbe Passwort identisch sein. Sein Zweck ist es, für die Überprüfung mittels matches(...) geeignet zu sein.

Eine kleine Tabelle zur visuellen Verankerung:

Begriff Was ist das Wo kommt es vor Lebensdauer Beispiel
rawPassword ursprüngliche Benutzereingabe Eingabeanfrage Sekunden/Millisekunden "qwerty123"
storedPassword Ergebnis der sicheren Aufbereitung für die Speicherung User Store (Speicher/DB) Wochen/Jahre "{bcrypt}$2a$10$..."

Hier ist übrigens die erste „pädagogische Mine“ versteckt: Wenn Sie alles einfach password nennen, vergessen Sie nach einer Woche selbst, wo das Klartext-Passwort und wo das gespeicherte ist. Daher ist es besser, im Code direkt zu benennen: rawPassword, storedPassword, passwordHash. Das ist keine Pedanterie – es ist Schutz vor Ihrer eigenen Unachtsamkeit am Freitagabend.

2. PasswordEncoder: encode und matches

Spring Security ist so aufgebaut, dass Sie nicht gezwungen sind, Kryptografie und String-Vergleiche manuell zu schreiben. Stattdessen führt es einen sehr einfachen Vertrag ein: PasswordEncoder. Die wichtige Idee ist, dass dieser Vertrag Teil der gesamten Authentifizierungsarchitektur ist: DaoAuthenticationProvider überprüft das Passwort genau über PasswordEncoder. Das heißt, wenn Sie den Encoder richtig ausgewählt und konfiguriert haben, schreiben Sie keine „Passwort-Überprüfung“ in Controllern und erfinden nicht Ihr eigenes Mini-Spring-Security auf dem Knie.

Das Interface PasswordEncoder kann man sich (auf Anwendungsebene) als „zwei Knöpfe“ merken:

— encode(rawPassword) – das Passwort für die Speicherung vorbereiten;
— matches(rawPassword, storedPassword) – überprüfen, ob die Eingabe mit dem Gespeicherten übereinstimmt.

Genau in dieser Reihenfolge werden wir den Lebenszyklus betrachten:

flowchart TD
    A["raw password
(Benutzereingabe)"] B["PasswordEncoder.encode(...)"] C["stored password
({id}... Zeichenkette)"] D["PasswordEncoder.matches(...)"] E["true/false"] A --> B --> C A --> D C --> D --> E

Das heißt, encode(...) ist keine „Überprüfung“, sondern das „Erzeugen des gespeicherten Werts“. Und matches(...) ist kein „String-Vergleich“, sondern eine „Überprüfung gemäß den Regeln des Algorithmus“, inklusive Salt, Cost Factor und aller Interna.

Und ja, das Wort encode kann einen Neuling verwirren. Es bedeutet nicht „wieder lesbar machen“. Es ist eher „in ein sicheres Speicherformat kodieren“. Als ob Sie den Text nicht „in eine andere Sprache übersetzt“, sondern „in einen Safe mit Siegel verpackt“ hätten.

3. encode(...): Wie das Ergebnis aussieht

Anfänger haben den sehr menschlichen Wunsch: „Okay, wir speichern das Passwort als String. Also muss man zum Überprüfen das eingegebene Passwort ebenfalls durch encode(...) laufen lassen und dann die beiden Strings vergleichen. Logisch, oder?“ Logisch – als Idee. Aber technisch ist das für die meisten vernünftigen Passwort-Speicher-Algorithmen falsch. Und genau deshalb gibt Ihnen Spring Security matches(...), damit Sie nicht versuchen, „neu zu berechnen und zu vergleichen“, wo das nicht funktioniert.

In sicheren Passwort-Algorithmen gibt es normalerweise Salt und andere Parameter, daher kann dasselbe Passwort bei jedem encode(...) unterschiedliche Zeichenketten ergeben. Das ist kein Bug. Das ist ein Feature. Und wenn Sie versuchen, die Ergebnisse von encode(...) mittels equals(...) zu vergleichen, treiben Sie sich selbst in die Enge.

Mini-Demo in Java (in der Konsole – zu Lehrzwecken; in einer echten Anwendung drucken wir Passwörter natürlich nicht so aus):

import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

// Einen "Standard"-DelegatingPasswordEncoder mit den üblichen Algorithmen und dem {id}-Präfix nehmen
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

// Dasselbe Raw-Passwort zweimal codieren: aufgrund des Salts ist das Ergebnis unterschiedlich
String first = encoder.encode("qwerty123");
String second = encoder.encode("qwerty123");

// Der Vergleich der Strings ist hier als Idee der Passwortprüfung nicht korrekt – das wird durch die Ausgabe demonstriert
System.out.println(first.equals(second)); // false (und das ist normal)

In diesem Moment ist es nützlich, innezuhalten und sich zu sagen: „Okay, das Speichern von Passwörtern ist keine Funktion, die immer das gleiche Ergebnis liefert“. Es ist wie ein Fingerabdruck, der mit verschiedenen Scannern aufgenommen wurde: im Prinzip geht es um dasselbe, aber die Darstellung kann unterschiedlich sein – die Authentizitätsprüfung funktioniert jedoch.

Daraus folgt die praktische Schlussfolgerung: Sie rufen encode(...) auf, wenn Sie ein Passwort erstellen oder ändern (z. B. bei der Benutzerregistrierung oder Passwortänderung). Sie verwenden encode(...) jedoch nicht als Teil der Überprüfung beim Login.

4. matches(...): Passwort-Überprüfung

Wenn ein Student zum ersten Mal matches(raw, stored) sieht, hat er oft das Gefühl: „Das ist doch nur ein praktischer Wrapper um equals(...)“. Tatsächlich ist das ein wichtiger technischer Punkt: Hier verbirgt sich die Logik, die sowohl den Salt als auch die Algorithmusparameter und den korrekten Vergleich (einschließlich des zeitlich sicheren Vergleichs) berücksichtigt. Wenn encode(...) die Vorbereitung ist, dann ist matches(...) die Authentizitätsprüfung nach den Regeln eines „Banktresors“, nicht nach den Regeln „zweier gleicher Texte“.

Das einfachste Szenario sieht so aus: Sie speichern stored (das, was Sie irgendwann über encode(...) erhalten haben), und beim Login vergleichen Sie die Eingabe des Benutzers mit diesem stored über matches(...).

import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

// Denselben DelegatingPasswordEncoder verwenden, der auch in der Anwendung benutzt wird
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

// stored ist genau das, was Sie in die DB / den Speicher legen (zusammen mit dem {id}-Präfix)
String stored = encoder.encode("qwerty123");

// matches(...) ist die einzig korrekte Methode, raw gegen stored zu prüfen
System.out.println(encoder.matches("qwerty123", stored)); // true
System.out.println(encoder.matches("wrong", stored));     // false

Hier sind zwei Kleinigkeiten wichtig, die später Stunden des Debuggens sparen.

Erstens: die Reihenfolge der Argumente. matches(raw, stored) liest sich als „Stimmt das eingegebene Passwort mit der gespeicherten Repräsentation überein?“. Wenn Sie die Reihenfolge vertauschen, haben Sie nicht „manchmal funktioniert es“, sondern seltsamen, schwer erklärbaren Unsinn.

Zweitens: stored muss genau das sein, was Sie speichern. Nicht „ein Stück des Hashes“, nicht „Hash ohne Präfix“, nicht „wir haben den String auf 60 Zeichen gekürzt, weil es hübscher aussieht“. Speichern Sie das, was der Encoder geliefert hat, und überprüfen Sie mit dem, was der Encoder geliefert hat. Ohne Kreativität.

5. Ein PasswordEncoder als Spring Bean

Nachdem Sie die API kennengelernt haben, kommt die nächste Versuchung: „Okay, verstanden. Dann schreibe ich an den benötigten Stellen new BCryptPasswordEncoder() und fertig.“ Und hier beginnt die stille Architekturkatastrophe. Nicht weil BCryptPasswordEncoder schlecht ist, sondern weil „Encoder ad hoc erstellen“ der Weg zu Inkonsistenz, zufälligen Einstellungen, unterschiedlichen Speicherformaten und der Unmöglichkeit, das Passwortformat später sicher zu migrieren, ist.

In unserem Projekt Secure Content Platform API haben wir bereits eine wichtige Entscheidung getroffen: Die Sicherheitskonfiguration soll explizit und lesbar sein (das haben wir in den letzten Tagen durch einen expliziten SecurityFilterChain gemacht). Bei PasswordEncoder ist die Logik dieselbe: ein Encoder-Baseline pro Anwendung, und er lebt als Spring Bean. Dann verwenden sowohl Spring Security als auch Ihr Code dieselbe Wahrheitsquelle.

Eine minimale (und völlig in Ordnung) Variante in der Konfiguration:

import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

// In Ihrer @Configuration-Klasse (z. B. SecurityConfig)
@Bean
PasswordEncoder passwordEncoder() {
    // Einheitliche Wahrheitsquelle für das Codieren/Überprüfen von Passwörtern in der gesamten Anwendung
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

Beachten Sie einen wichtigen methodischen Punkt: Wir binden die Anwendung nicht „für immer“ an eine einzelne Encoder-Klasse im Geschäftscode. Wir sagen: „Hier ist der Vertrag PasswordEncoder, hier ist seine Implementierung auf Konfigurationsebene.“ Das ist wie eine Steckdose an der Wand: Es ist Ihnen egal, welches Kabel wo in der Wand verlegt ist, solange die Steckdose funktioniert und dem Standard entspricht.

Und ja, Spring Security wird diesen Bean später automatisch für seine Authentifizierungsprovider übernehmen. Das heißt, Sie haben nicht nur ein „praktisches Objekt erstellt“, sondern einen korrekten Passwort-Mechanismus in den allgemeinen Authentifizierungsablauf integriert.

6. DelegatingPasswordEncoder und der Wechsel von Algorithmen

Nun – der „erwachsenste“ Teil dieser Vorlesung, aber ohne schweren Enterprise-Pathos. Die reale Welt lebt nicht ewig mit einem einzigen Passwort-Algorithmus. Heute starten Sie mit bcrypt, in einem Jahr entscheiden Sie sich für argon2, zwei Jahre später kaufen Sie ein Produkt mit einer Legacy-Tabelle, in der pbkdf2 steht. Die Option „lasst uns einfach den Encoder wechseln und alles neu berechnen“ klingt gut, ist technisch aber oft unmöglich, weil Sie die ursprünglichen Passwörter der Benutzer nicht kennen. Und das ist gut so: Sie sollten sie auch nicht kennen.

Sie brauchen also ein System, das verschiedene Formate gespeicherter Passwörter überprüfen kann, ohne den Code in ein „if-else über Strings“ zu verwandeln und ohne Sie zu zwingen, eine Menagerie von Encodern an jedem Punkt zu unterhalten. Dafür hat Spring Security den DelegatingPasswordEncoder erfunden.

Die Idee ist sehr einfach: In der gespeicherten Zeichenkette ist eine kleine „Formatmarkierung“ enthalten. Anhand dieser Markierung wählt der DelegatingPasswordEncoder aus, mit welchem Algorithmus (mit welchem konkreten PasswordEncoder) das Passwort überprüft werden soll.

Stellen Sie sich den DelegatingPasswordEncoder wie einen Dispatcher am Flughafen vor. Er selbst „fliegt“ nicht, aber er schaut auf die Flugnummer (das {id}-Präfix) und leitet den Passagier zum richtigen Gate (dem richtigen Encoder). Wenn Sie ihm statt der Flugnummer ein Blatt Papier ohne Kopfzeile bringen, sagt der Dispatcher ehrlich: „Ich weiß nicht, was ich damit machen soll“.

7. Das {id}-Format und die vollständige Speicherung der Zeichenkette

Wenn Sie PasswordEncoderFactories.createDelegatingPasswordEncoder() verwenden, sieht das Ergebnis von encode(...) normalerweise wie eine Zeichenkette aus, die mit {id} beginnt. Zum Beispiel {bcrypt}. Danach folgt der „Körper“ des gespeicherten Werts, der Salt, Cost und andere Parameter enthalten kann. Für einen Anfänger sieht das aus wie „ein langer String“, aber für Spring Security ist es ein strukturiertes Format, das nicht manuell zerbrochen werden darf.

Wichtiger Grundsatz: Die Zeichenkette muss vollständig gespeichert werden, als ein einheitliches Artefakt. Trennen Sie nicht {bcrypt} in eine separate Spalte. Entfernen Sie nicht das Präfix, „damit es hübscher aussieht“. Kürzen Sie die Zeichenkette nicht. „Normalisieren“ Sie sie nicht in Kleinbuchstaben. Ein Passwort-Hash ist keine Telefonnummer, er benötigt keine Formatierung.

Schauen wir uns ein Beispiel an (wieder: wir geben es nur zu Lehrzwecken auf der Konsole aus, um das Format zu sehen):

import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

// Der DelegatingPasswordEncoder fügt selbst das {id}-Präfix in den encoded-String ein
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

// Das, was encode(...) zurückgibt, ist das „gespeicherte Artefakt“ (vollständig)
String stored = encoder.encode("qwerty123");

// Zu Lehrzwecken sehen wir uns das Format an: wir werden ein Präfix wie {bcrypt} sehen
System.out.println(stored); // {bcrypt}$2a$10$... (jedes Mal anders)

Wenn Sie versuchen, das Präfix abzuschneiden und nur den „langen Teil“ zu behalten, wird DelegatingPasswordEncoder bei matches(...) in der Regel nicht erkennen können, um welches Format es sich handelt, und eine Exception werfen. Das ist keine Bösartigkeit des Frameworks. Es ist Schutz vor der Situation „wir wissen nicht, womit das codiert wurde, also versuchen wir es zu erraten“ (und Raten ist in der Sicherheit ein schlechter Business-Plan).

Um es noch einmal zu festigen, können wir die Delegation an einem Beispiel zeigen. Wir erstellen manuell einen BCrypt-Hash, kleben das Präfix {bcrypt} dran und bitten dann den DelegatingPasswordEncoder, das Passwort zu überprüfen:

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

// Der delegierende Encoder wählt die Implementierung anhand des {id}-Präfixes
PasswordEncoder delegating = PasswordEncoderFactories.createDelegatingPasswordEncoder();

// Demonstrationsmechanik: {bcrypt} an das Ergebnis von BCrypt angehängt
// In einer echten Anwendung macht man das normalerweise nicht per Hand – nur um das Prinzip zu verstehen
String stored = "{bcrypt}" + new BCryptPasswordEncoder().encode("qwerty123");

// Der DelegatingPasswordEncoder sieht {bcrypt} und leitet die Überprüfung an den BCryptPasswordEncoder weiter
System.out.println(delegating.matches("qwerty123", stored)); // true

Dieses Beispiel ist nicht wichtig, weil „man Präfixe von Hand kleben kann“ (in einer normalen Anwendung macht man das nicht), sondern weil es die Mechanik demonstriert: Das Präfix ist ein Hinweis darauf, welcher Encoder verwendet werden soll.

8. createDelegatingPasswordEncoder() als Basislinie

Wenn Sie gerade anfangen, möchte man „den richtigen Algorithmus sofort und für immer auswählen“. Aber auf fundamentaler Ebene ist etwas anderes nützlicher: eine korrekte Basislinie zu wählen, die eine vernünftige Sicherheit bietet und einen nicht ohne Migrationsmöglichkeit an ein einzelnes Format bindet. Genau deshalb gibt es in Spring Security PasswordEncoderFactories.createDelegatingPasswordEncoder() – eine praktische Factory, die einen fertigen DelegatingPasswordEncoder mit Standard-Mappings zurückgibt.

Praktisch bedeutet das: Wir legen einen einzigen PasswordEncoder-Bean fest, und dann verwendet das gesamte Projekt (sowohl Spring Security als auch unser Code) ihn als einzige Wahrheitsquelle. Das ist dieselbe Basislinie, unabhängig davon, woher die Anwendung ihre Benutzer bezieht: aus dem Speicher, aus der Datenbank oder einem anderen User Store. Aber heute ist für uns etwas anderes wichtig: zu sehen, dass wir einen einzigen „zentralen Prüfer“ haben, dem es egal ist, welcher Algorithmus hinter dem jeweiligen gespeicherten Passwort steckt.

Man kann sogar eine kleine „Selbsttest“-Demo direkt im Projektcode machen (z. B. temporär über CommandLineRunner), um zu sehen, dass das encodierte Passwort immer mit {...} beginnt:

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.password.PasswordEncoder;

// Innerhalb einer @Configuration-Klasse
@Bean
CommandLineRunner showPasswordFormat(PasswordEncoder encoder) {
    return args -> {
        // Demonstration des Formats der encodierten Zeichenkette: wir sehen das {id}-Präfix, z. B. {bcrypt}
        System.out.println(encoder.encode("qwerty123")); // {bcrypt}$2a$10$...
    };
}

Noch einmal: Das ist eine Demonstration. Wir machen keinen „Passwort-Generator über Logs“ als Produktfeature. Aber zum Verständnis des Formats funktioniert das hervorragend: Sie sehen mit eigenen Augen das Präfix {bcrypt}, und dann fällt es Ihnen leichter zu verstehen, warum wir überhaupt über DelegatingPasswordEncoder sprechen.

9. PasswordService als einheitliche Tür

Wenn der Mechanismus klar ist, stellt sich die nächste praktische Frage: Wo soll die Arbeit mit Passwörtern in Ihrer Anwendung untergebracht werden, damit Sie morgen nicht versehentlich das Raw-Passwort loggen oder eine doppelte Codierung vornehmen? Auf fundamentaler Ebene ist die gute Antwort einfach: Bauen Sie einen kleinen Service-Wrapper, der zur „einzigen Tür“ für Passwortoperationen wird. Das ist keine Magie, sondern Disziplin: Je weniger Stellen im Code etwas über Passwörter wissen, desto geringer die Wahrscheinlichkeit, etwas Dummes zu tun.

Eine minimale Version eines solchen Services im Projekt könnte so aussehen:

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class PasswordService {
    private final PasswordEncoder encoder;

    // Den gemeinsamen PasswordEncoder aus dem Spring-Kontext einspritzen, nicht manuell erstellen
    public PasswordService(PasswordEncoder encoder) {
        this.encoder = encoder;
    }

    // Der explizite Methodenname betont den Lebenszyklus: "für die Speicherung vorbereiten"
    public String encodeForStorage(String rawPassword) {
        return encoder.encode(rawPassword);
    }

    // Die einzig korrekte Überprüfung von raw gegen stored – über matches(...)
    public boolean matches(String rawPassword, String storedPassword) {
        return encoder.matches(rawPassword, storedPassword);
    }
}

Beachten Sie, wie das hilft, den Code zu lesen. An jeder Stelle im Projekt sehen Sie encodeForStorage – das ist der Moment der Passwort-Erstellung/Änderung. Sie sehen matches – das ist eine Überprüfung. Und dabei verteilen Sie nicht PasswordEncoderFactories im Code, erstellen den Encoder nicht von Hand und streiten nicht mit sich selbst, welcher Algorithmus „wohl der richtige wäre“.

Und noch ein wichtiger Punkt: Dieser Service ist ein geeigneter Ort, um später zentral zu überwachen, dass das Passwort nirgendwo in Logs ausläuft. Selbst wenn Sie jetzt nichts loggen, ist es besser, einen offensichtlichen Punkt zu haben, an dem Sie Schutzprüfungen hinzufügen können, als später zu versuchen, „versehentliche printlns“ im ganzen Projekt zu bereinigen.

10. Typische Fehler im Umgang mit PasswordEncoder

Fehler Nr. 1: Passwortprüfung über encode + equals.
Das führt fast immer dazu, dass der Login „plötzlich“ nicht funktioniert. Der Grund ist, dass encode(...) jedes Mal ein neues Ergebnis aufgrund des Salts liefern kann. Die Verifikation darf nur über matches(raw, stored) erfolgen, sonst vergleichen Sie nicht den Sinn, sondern zufällig erzeugte Zeichenketten.

Fehler Nr. 2: Vertauschen der Argumente von matches(...).
Bei Anfängern passiert das häufiger, als man denkt, besonders wenn die Variablen vage benannt sind (password1, password2). Verwenden Sie aussagekräftige Namen wie rawPassword und storedPassword und merken Sie sich die Semantik: „raw stimmt mit stored überein“. Dann lesen Sie den Aufruf auch ohne IDE korrekt.

Fehler Nr. 3: Nicht die gesamte encodierte Zeichenkette speichern, sondern „nur den Hash“.
Wenn Sie DelegatingPasswordEncoder verwenden, ist das Präfix {id} Teil des Formats. Entfernen Sie es, kürzen Sie die Zeichenkette, „bereinigen“ Sie sie – und Sie erhalten entweder eine Exception oder fangen an zu notlösen „lasst uns immer annehmen, das sei bcrypt“. Das ist genau das manuelle Chaos, vor dem Spring Security Sie bewahren will.

Fehler Nr. 4: Encoder „ad hoc“ in verschiedenen Klassen erstellen.
Heute machen Sie new BCryptPasswordEncoder(), morgen fügt jemand new BCryptPasswordEncoder(12) hinzu, übermorgen beginnen Sie, DelegatingPasswordEncoder zu verwenden – und schon haben Sie drei verschiedene Welten, die Passwörter unterschiedlich speichern und überprüfen. Der Encoder sollte einer sein (als Bean), und die Anwendung sollte vom Interface PasswordEncoder abhängen.

Fehler Nr. 5: Das Raw-Passwort loggen (oder in Exceptions/API-Antworten aufnehmen).
Das klingt banal, kommt aber in Lernprojekten ständig vor: „Ach, ich habe nur einen Debug gemacht“. Das Problem ist, dass Logs lange leben, von anderen Menschen gelesen werden und in zentrale Systeme wandern. Das Passwort darf in keiner Form dorthin gelangen. Nicht einmal als „temporärer Debug“.

1
Aufgabe
Spring Security, Level 6, Lektion 2
Gesperrt
Allgemeiner PasswordEncoder als Spring Bean
Allgemeiner PasswordEncoder als Spring Bean
1
Aufgabe
Spring Security, Level 6, Lektion 2
Gesperrt
Format `{id}` und Speicherung der vollständigen Zeichenkette
Format `{id}` und Speicherung der vollständigen Zeichenkette
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION