Loslegen
Javaneer
Zurück zur Stufe
Modul 4·Sicherheit

Benutzer & Passwort-Hashing

UserDetailsService, Benutzer speichern und warum Passwörter immer mit BCrypt gesalzen und gehasht werden müssen.

16 Min. LesezeitFortgeschritten

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Access rules decide what a role may do - but where do users, their passwords, and their roles actually come from? And how do you store a password without creating a disaster waiting to be leaked? This lesson covers both.

Never store passwords as they are

The single most important rule in application security: never store a password in a form you can read back. Not plain text, not reversible encryption. If your database leaks - and databases leak - every account must still be safe.

The answer is a one-way hash: a function that turns a password into a scrambled value it's computationally infeasible to reverse. On login you hash the submitted password and compare hashes.

Hashing is a paper shredder, encryption is a safe

Encryption is a safe: with the key, you can open it and read what's inside - so whoever holds the key holds the passwords. Hashing is a paper shredder: you can shred a document and later shred another to check if the confetti matches, but you can never un-shred it back into the original. Passwords belong in the shredder, not the safe - there's no legitimate reason to ever read them back.

Use BCrypt (or Argon2)

Not any hash will do. A fast, plain hash (MD5, SHA-256) can be brute-forced billions of times a second and doesn't defend against identical passwords hashing identically. Use a password hashing function built for the job - BCrypt - which is deliberately slow and automatically salts each hash:

@Bean
PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();   // slow by design, salted per password
}
String hash = passwordEncoder.encode(rawPassword);        // at registration
boolean ok  = passwordEncoder.matches(rawPassword, hash); // at login

The stored hash includes its own salt, so two users with the same password get different hashes, and precomputed "rainbow table" attacks don't work.

Telling Spring where users live: UserDetailsService

Spring Security loads users through a UserDetailsService - one method that finds a user by username and returns their stored (hashed) password and authorities:

@Bean
UserDetailsService userDetailsService(MemberRepository members) {
    return email -> members.findByEmail(email)
        .map(m -> User.withUsername(m.getEmail())
            .password(m.getPasswordHash())      // the BCrypt hash from the DB
            .roles(m.getRole().name())          // MEMBER or LIBRARIAN
            .build())
        .orElseThrow(() -> new UsernameNotFoundException(email));
}

Spring calls this during authentication, then uses the PasswordEncoder to check the submitted password against the stored hash. You never compare passwords yourself.

Hash on the way in, once

Encode the password with the PasswordEncoder when you create or change a user, and store only the hash. The raw password should exist in memory for a moment and never be logged, returned in a response, or written to the database.

Store a new member safely

When BookVault registers a member with a chosen password, describe exactly what gets stored in the database and what does not - and explain why using BCrypt instead of a plain SHA-256 hash matters even though both are "hashes."

How should user passwords be stored, and with what?

Key takeaways

  • Never store passwords readably: use a one-way hash you can't reverse, not plain text or reversible encryption.
  • Use BCrypt (or Argon2): deliberately slow and automatically salted, defeating brute-force and rainbow-table attacks.
  • Encode with PasswordEncoder.encode() at registration; verify with matches() at login - never compare passwords yourself.
  • A UserDetailsService loads a user (hashed password + authorities) by username for Spring to authenticate.
  • The raw password should live only momentarily in memory - never logged, returned, or stored.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten