Zum Hauptinhalt springen

Woche 6, Tag 1 — Spring Security Mental Model

Ziel

Heute verstehst du das Mental Model von Spring Security.

Die Kernfragen:

  1. Welches Problem löst Spring Security?
  2. Was ist Authentifizierung?
  3. Was ist Autorisierung?
  4. Was ist der Unterschied zwischen Authentifizierung und Autorisierung?
  5. Warum nutzt Spring Security Filter?
  6. Was ist SecurityFilterChain?
  7. Was ist FilterChainProxy?
  8. Was ist SecurityContextHolder?
  9. Was passiert, wenn du spring-boot-starter-security hinzufügst?
  10. Wie passt du Security-Regeln an?
  11. Was ist PasswordEncoder?
  12. Was sind typische Prüfungsfallen?

1. Kurz-Wiederholung aus Woche 5

In Woche 5 hast du gelernt:

  • Der Controller verarbeitet HTTP.
  • Der Service enthält Business-Logik und Transaktionen.
  • Das Repository kümmert sich um Persistenz.
  • Die Entity mappt auf die Datenbank.
  • Das DTO mappt auf die API.
  • @Transactional definiert Transaktionsgrenzen.
  • REST-APIs sollten in der Regel DTOs zurückgeben, keine Entities.

Merksatz:


Controller -> Service -> Repository -> Datenbank

Jetzt setzt du Security vor den Controller.

Spring Security arbeitet, bevor der Request deinen Controller erreicht.

Merksatz:


Security-Prüfungen laufen vor der Controller-Logik.

2. Welches Problem löst Spring Security?

Die meisten Anwendungen brauchen Security.

Beispiele:


Wer ist der Benutzer?
Ist der Benutzer eingeloggt?
Ist das Passwort korrekt?
Darf dieser Benutzer diesen Endpunkt aufrufen?
Darf dieser Benutzer diese Aktion ausführen?
Soll dieser Request blockiert werden?
Soll CSRF-Schutz angewendet werden?
Soll eine Session erstellt werden?
Soll ein JWT-Token akzeptiert werden?

Spring Security hilft bei:


Authentifizierung
Autorisierung
Passwort-Codierung
Security-Filter
Login/Logout
HTTP Basic
Formular-Login
CSRF-Schutz
Session-Verwaltung
Method Security
OAuth2 / JWT-Unterstützung
Schutz vor typischen Angriffen

Kurze Definition:

Spring Security ist das Spring-Framework für Authentifizierung, Autorisierung und gängige Security-Schutzmaßnahmen.

Merksatz:

Spring Security schützt Requests, bevor sie die Controller erreichen.


3. Authentifizierung

Authentifizierung beantwortet:


Wer bist du?

Beispiele:


Benutzername und Passwort
Session-Cookie
JWT-Token
OAuth2-Login
API-Key
Zertifikat

Wenn die Authentifizierung erfolgreich ist, kennt Spring Security den aktuellen Benutzer.

Beispiel:


Benutzer: steve@example.com
Rollen: USER, ADMIN
Authentifiziert: true

Merksatz:

Authentifizierung bedeutet, die Identität nachzuweisen.


4. Autorisierung

Autorisierung beantwortet:


Was darfst du tun?

Beispiele:


Darf dieser Benutzer GET /api/tasks aufrufen?
Darf dieser Benutzer einen Task löschen?
Darf dieser Benutzer Admin-Endpunkte aufrufen?
Darf dieser Benutzer die Daten eines anderen Mandanten sehen?
Darf dieser Benutzer eine Rechnung freigeben?

Autorisierung passiert nach der Authentifizierung.

Beispiel:


Authentifizierter Benutzer: steve@example.com
Rolle: USER
Endpunkt: DELETE /api/admin/users/10
Entscheidung: verweigert

Merksatz:

Autorisierung bedeutet, Berechtigungen zu prüfen.


5. Authentifizierung vs. Autorisierung

KonzeptFrageBeispiel
AuthentifizierungWer bist du?Login mit Benutzername und Passwort
AutorisierungWas darfst du tun?Nur ADMIN darf Benutzer löschen

Merksatz:


Authentifizierung = Identität.
Autorisierung = Berechtigung.

Interview-Satz:

Authentifizierung kommt zuerst. Autorisierung entscheidet, ob der authentifizierte Benutzer etwas aufrufen darf.


6. 401 vs. 403

Das ist sehr wichtig.

401 Unauthorized

Bedeutet:


Du bist nicht authentifiziert.

Beispiele:


fehlendes Token
ungültiges Token
abgelaufenes Token
nicht eingeloggt

403 Forbidden

Bedeutet:


Du bist authentifiziert, aber nicht berechtigt.

Beispiele:


normaler Benutzer versucht Admin-Endpunkt aufzurufen
Benutzer versucht auf Daten eines anderen Mandanten zuzugreifen
Mitarbeiter versucht Task ohne Berechtigung freizugeben

Merksatz:


401 = wer bist du?
403 = ich weiß, wer du bist, aber du darfst nicht.

7. Wo sitzt Spring Security?

Request-Ablauf ohne Security:


Client -> DispatcherServlet -> Controller -> Service -> Repository

Request-Ablauf mit Security:


Client -> Spring Security Filter -> DispatcherServlet -> Controller -> Service -> Repository

Spring Security prüft den Request, bevor die Spring-MVC-Controller-Logik läuft.

Merksatz:

Spring Security sitzt vor Spring MVC.


8. Warum Filter?

In Servlet-Webanwendungen können Filter HTTP-Requests abfangen, bevor sie den Servlet erreichen.

Spring MVC nutzt:


DispatcherServlet

Spring Security nutzt:


Servlet-Filter

Ein Filter kann:


Request-Header lesen
Session prüfen
Token prüfen
Benutzer authentifizieren
Request ablehnen
Request weiterlaufen lassen
Security Context setzen
Security Context löschen

Merksatz:

Filter sind ideal für Security, weil sie vor den Controllern laufen.


9. Servlet-Filterkette

Ein Request durchläuft eine Kette von Filtern.

Einfaches Bild:


HTTP-Request

Filter 1

Filter 2

Filter 3

DispatcherServlet

Controller

Spring Security fügt seine eigene Security-Filterkette hinzu.

Beispiel-Security-Filter können Folgendes handhaben:


CSRF
CORS
Logout
Benutzername/Passwort-Login
Basic Authentication
Bearer-Token-Authentifizierung
Session-Verwaltung
Autorisierung
Exception Handling

Merksatz:

Spring Security basiert hauptsächlich auf Filterketten.


10. Was ist SecurityFilterChain?

SecurityFilterChain ist die geordnete Menge von Security-Filtern, die auf passende HTTP-Requests angewendet werden.

Kurze Definition:

SecurityFilterChain definiert, welche Security-Filter und Security-Regeln für Requests gelten.

In modernem Spring Security definierst du in der Regel einen Bean:


@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
}

Bedeutung:


öffentliche Endpunkte sind offen
alle anderen Requests erfordern Authentifizierung
HTTP Basic ist aktiviert

Merksatz:

In SecurityFilterChain konfigurierst du Web-Security-Regeln.


11. HttpSecurity

HttpSecurity ist das Builder-Objekt zur Konfiguration von Servlet-Security.

Beispiel:


@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}

HttpSecurity konfiguriert unter anderem:


Autorisierungsregeln
Login-Methode
Logout
CSRF
CORS
Sessions
Exception Handling
Header
OAuth2 Resource Server
JWT

Merksatz:

HttpSecurity baut die SecurityFilterChain.


12. Was ist FilterChainProxy?

Spring Security hat viele Filter.

Der Servlet-Container sieht aber in der Regel nur einen zentralen Spring-Security-Filter-Einstiegspunkt.

Innerhalb von Spring Security delegiert FilterChainProxy an die passende SecurityFilterChain.

Einfaches Bild:


Servlet-Container

DelegatingFilterProxy

FilterChainProxy

SecurityFilterChain

Security-Filter

Kurze Definition:

FilterChainProxy ist die Spring-Security-Komponente, die Requests an die passende Security-Filterkette delegiert.

Im täglichen Coding konfigurierst du in der Regel SecurityFilterChain, nicht direkt FilterChainProxy.

Merksatz:

FilterChainProxy leitet Requests durch Spring-Security-Filterketten.


13. Security vor dem DispatcherServlet

Der Request durchläuft normalerweise Security-Filter, bevor er Spring MVC erreicht.

Ablauf:


1. HTTP-Request kommt an.
2. Servlet-Container startet die Filterkette.
3. Spring-Security-Filter laufen.
4. Authentifizierung kann stattfinden.
5. Autorisierungsentscheidung wird getroffen.
6. Wenn erlaubt, läuft der Request weiter.
7. DispatcherServlet empfängt den Request.
8. Controller-Methode wird ausgeführt.

Wenn der Request abgelehnt wird:


Controller-Methode wird nicht ausgeführt

Merksatz:

Wenn Spring Security den Request blockiert, wird der Controller nie aufgerufen.


14. Standard-Security in Spring Boot

Wenn du hinzufügst:


implementation("org.springframework.boot:spring-boot-starter-security")

konfiguriert Spring Boot grundlegende Security automatisch, wenn du keine eigene Konfiguration bereitstellst.

Typisches Standardverhalten in einer Web-App:


alle Endpunkte sind geschützt
ein generiertes Passwort wird in den Logs ausgegeben
ein Standardbenutzer namens user existiert
Formular-Login kann für Browser-Requests verfügbar sein
HTTP Basic kann für API-Clients verfügbar sein

Das überrascht viele Einsteiger.

Merksatz:

Der Security-Starter ändert die gesamte Anwendung sofort.


15. Generiertes Passwort

Nach dem Hinzufügen von Spring Security siehst du möglicherweise ein Log wie:


Using generated security password: ...

Das ist nur für die Entwicklung gedacht.

Der Standard-Benutzername ist üblicherweise:


user

Das generierte Passwort ändert sich bei jedem Neustart.

In echten Apps konfigurierst du Benutzer ordentlich.

Merksatz:

Das generierte Passwort ist ein Entwicklungs-Default, keine Produktions-Security.


16. Erste Security-Konfiguration

Einfaches REST-API-Security-Beispiel:


@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
}

Bedeutung:


/api/auth/** ist öffentlich
/actuator/health ist öffentlich
alles andere erfordert Authentifizierung
HTTP Basic ist aktiviert
CSRF ist deaktiviert

Wichtig:


Kopiere csrf.disable() nicht blind für jede App.
Für zustandslose APIs ist es üblich.
Für Browser-Session-Apps ist CSRF-Schutz wichtig.

CSRF schauen wir uns später an.


17. authorizeHttpRequests

authorizeHttpRequests konfiguriert Autorisierungsregeln für HTTP-Requests.

Beispiel:


.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/tasks/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)

Bedeutung:


öffentliche Endpunkte sind offen
Admin-Endpunkte brauchen ADMIN-Rolle
GET tasks braucht USER oder ADMIN
alles andere braucht Authentifizierung

Merksatz:

authorizeHttpRequests steuert URL-basierte Autorisierung.


18. Die Reihenfolge der Regeln ist wichtig

Security-Regeln werden in Reihenfolge geprüft.

Beispiel:


.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)

Gut.

Aber das ist problematisch:


.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
)

Warum?


anyRequest matcht alles.
Die Admin-Regel danach wird unerreichbar oder ungültig.

Merksatz:

Setze spezifische Autorisierungsregeln vor allgemeine Regeln.


19. Häufige Autorisierungsmethoden

Beispiele:


permitAll()
denyAll()
authenticated()
hasRole("ADMIN")
hasAnyRole("USER", "ADMIN")
hasAuthority("TASK_READ")
hasAnyAuthority("TASK_READ", "TASK_WRITE")

Bedeutungen:

MethodeBedeutung
permitAll()jeder darf zugreifen
denyAll()niemand darf zugreifen
authenticated()Benutzer muss authentifiziert sein
hasRole("ADMIN")Benutzer muss Rolle ADMIN haben
hasAuthority("TASK_READ")Benutzer muss Berechtigung TASK_READ haben

20. Rolle vs. Berechtigung

Spring Security arbeitet mit Berechtigungen (Authorities).

Eine Rolle wird üblicherweise als Berechtigung mit Präfix dargestellt:


ROLE_

Beispiel:


hasRole("ADMIN")

prüft auf Berechtigung:


ROLE_ADMIN

Beispiel:


hasAuthority("ROLE_ADMIN")

prüft direkt auf:


ROLE_ADMIN

Wichtige Prüfungsfalle:


hasRole("ROLE_ADMIN")

Meist falsch, weil hasRole das Präfix ROLE_ hinzufügt.

Richtig:


hasRole("ADMIN")

oder:


hasAuthority("ROLE_ADMIN")

Merksatz:

hasRole("ADMIN") bedeutet Berechtigung ROLE_ADMIN.


21. Authentication-Objekt

Nach erfolgreicher Authentifizierung speichert Spring Security Authentifizierungsinformationen.

Wichtiges Konzept:


Authentication

Es enthält üblicherweise:


principal
credentials
authorities
authenticated flag
details

Beispiel:


principal = aktueller Benutzer
credentials = Passwort/Token, wird später meist gelöscht
authorities = Rollen/Berechtigungen
authenticated = true

Merksatz:

Authentication repräsentiert den aktuell authentifizierten Benutzer.


22. SecurityContext

SecurityContext hält die Authentication.

Einfaches Bild:


SecurityContext
└── Authentication
├── principal
├── credentials
└── authorities

Merksatz:

SecurityContext speichert Security-Informationen für den aktuellen Request.


23. SecurityContextHolder

SecurityContextHolder speichert den aktuellen SecurityContext.

Beispiel:


Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();

Dann kannst du holen:


String username = authentication.getName();
Collection<? extends GrantedAuthority> authorities =
authentication.getAuthorities();

Wichtig:


Der meiste Anwendungscode sollte SecurityContextHolder nicht überall manuell nutzen.
Bevorzuge Methodenparameter oder Service-Abstraktionen, wenn möglich.

Merksatz:

SecurityContextHolder gibt Zugriff auf den aktuellen Security Context.


24. Aktuellen Benutzer im Controller holen

Beispiel:


@GetMapping("/api/me")
public String me(Authentication authentication) {
return authentication.getName();
}

Spring kann die aktuelle Authentication in Controller-Methoden injizieren.

Beispiel mit Principal:


@GetMapping("/api/me")
public String me(Principal principal) {
return principal.getName();
}

Merksatz:

Controller können die aktuelle Authentication oder Principal empfangen.


25. Lebensdauer des SecurityContext

Für einen normalen Request:


1. Security-Filter lädt oder erstellt SecurityContext.
2. Authentication wird nach erfolgreichem Login/Token-Check gesetzt.
3. Controller/Service kann auf aktuellen Benutzer zugreifen.
4. Am Ende des Requests wird der Context gelöscht.

Warum löschen?


vermeide, dass Benutzerinformationen zwischen Requests/Threads durchsickern

Merksatz:

Der Security Context ist request-bezogen und muss nach der Request-Verarbeitung gelöscht werden.


26. Authentifizierungsablauf: Benutzername und Passwort

Vereinfachter Benutzername/Passwort-Ablauf:


1. Benutzer sendet Benutzername und Passwort.
2. Authentifizierungsfilter extrahiert Anmeldedaten.
3. AuthenticationManager versucht zu authentifizieren.
4. AuthenticationProvider prüft Benutzerdetails und Passwort.
5. Wenn gültig, wird Authentication erstellt.
6. SecurityContext speichert Authentication.
7. Request wird erlaubt, wenn Autorisierung passt.

Merksatz:

AuthenticationManager authentifiziert Anmeldedaten.


27. AuthenticationManager

Kurze Definition:

AuthenticationManager ist für die Authentifizierung eines Authentifizierungs-Requests zuständig.

Er empfängt ein nicht authentifiziertes Token, zum Beispiel Benutzername/Passwort.

Dann gibt er eine authentifizierte Authentication zurück oder wirft eine Exception.

Merksatz:

AuthenticationManager entscheidet, ob Anmeldedaten gültig sind.


28. AuthenticationProvider

AuthenticationProvider führt eine bestimmte Authentifizierungsstrategie aus.

Beispiele:


Benutzername/Passwort gegen Datenbank
LDAP-Authentifizierung
JWT-Token-Authentifizierung
OAuth2-Authentifizierung
vorgeauthentifizierter Benutzer

Für Benutzername/Passwort ist ein häufiger Provider:


DaoAuthenticationProvider

Er nutzt:


UserDetailsService
PasswordEncoder

Merksatz:

AuthenticationProvider weiß, wie man eine Art von Anmeldedaten authentifiziert.


29. UserDetailsService

UserDetailsService lädt Benutzerinformationen anhand des Benutzernamens.

Typische Methode:


UserDetails loadUserByUsername(String username);

Er liefert:


Benutzername
codiertes Passwort
Berechtigungen
Kontostatus

Beispiel:


@Service
public class DatabaseUserDetailsService implements UserDetailsService {

private final UserRepository userRepository;

public DatabaseUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public UserDetails loadUserByUsername(String email) {
UserEntity user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));

return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.build();
}
}

Merksatz:

UserDetailsService lädt Benutzer für die Authentifizierung.


30. PasswordEncoder

Speichere niemals Klartext-Passwörter.

Schlecht:


password = "secret123"

Gut:


Passwort-Hash = "$2a$10$..."

PasswordEncoder kümmert sich um Passwort-Hashing und -Vergleich.

Beispiel-Bean:


@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

Bei der Registrierung:


String hash = passwordEncoder.encode(rawPassword);

Beim Login:


passwordEncoder.matches(rawPassword, storedHash);

Merksatz:

Speichere Passwort-Hashes, keine Klartext-Passwörter.


31. Warum Passwort-Codierung einweg ist

Passwort-Codierung ist keine Verschlüsselung.

Verschlüsselung kann mit einem Schlüssel rückgängig gemacht werden.

Passwort-Hashing sollte einweg sein.

Bedeutung:


Klartext-Passwort -> Hash
Hash -> Klartext-Passwort sollte nicht möglich sein

Beim Login vergleicht Spring:


vom Benutzer eingegebenes Klartext-Passwort
gespeicherter Passwort-Hash

mit matches.

Merksatz:

PasswordEncoder prüft Passwörter, ohne sie zu dekodieren.


32. In-Memory-Benutzer-Beispiel

Zum Lernen:


@Configuration
public class SecurityConfig {

@Bean
UserDetailsService users(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user@example.com")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();

UserDetails admin = User.builder()
.username("admin@example.com")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Das ist nützlich für Demos und Tests.

In echten Apps kommen Benutzer üblicherweise aus Datenbank, LDAP, OAuth2 oder einem Identity Provider.

Merksatz:

In-Memory-Benutzer sind zum Lernen nützlich, nicht für echtes Produktions-Benutzermanagement.


33. Vollständige Basis-Security-Konfiguration


@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/tasks/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}

@Bean
UserDetailsService users(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user@example.com")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();

UserDetails admin = User.builder()
.username("admin@example.com")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Diese Konfiguration:


öffnet /api/public/**
erfordert ADMIN für /api/admin/**
erlaubt USER oder ADMIN für GET /api/tasks/**
erfordert Authentifizierung für alles andere
nutzt HTTP Basic
nutzt BCrypt-Passwort-Codierung

34. HTTP Basic

HTTP Basic sendet Anmeldedaten im Request-Header.

Beispiel-Header:


Authorization: Basic base64(username:password)

Wichtig:


Basic Authentication muss in echten Anwendungen HTTPS nutzen.

Warum?


Base64 ist keine Verschlüsselung.
Ohne HTTPS können Anmeldedaten offengelegt werden.

Merksatz:

HTTP Basic ist einfach, muss aber durch HTTPS geschützt werden.


35. Formular-Login

Formular-Login ist üblich für browserbasierte Anwendungen.

Spring Security kann eine Login-Seite generieren oder nutzen.

Ablauf:


1. Benutzer ruft geschützte Seite auf.
2. Benutzer wird zur Login-Seite weitergeleitet.
3. Benutzer sendet Benutzername/Passwort.
4. Spring authentifiziert den Benutzer.
5. SecurityContext wird gespeichert.
6. Benutzer macht mit Session-Cookie weiter.

Für REST-APIs ist Formular-Login oft nicht das gewünschte Verhalten.

REST-APIs bevorzugen oft:


HTTP Basic für einfache interne APIs
JWT Bearer Tokens
OAuth2 Resource Server
Session-basierter Login bei Browser-Apps

Merksatz:

Formular-Login ist browser-orientiert; APIs nutzen oft Token- oder Basic-Authentifizierung.


36. Session-basierte Security

Bei session-basierter Security:


Server speichert Authentifizierung in HTTP-Session
Browser speichert Session-Cookie
Cookie identifiziert Session bei künftigen Requests

Ablauf:


einmal einloggen
Server erstellt Session
Client sendet Session-Cookie
Server lädt Security Context aus Session

Gut für:


traditionelle Web-Apps
serverseitig gerenderte Apps
einige Browser-Apps

Merksatz:

Session-Security speichert den Login-Zustand auf dem Server.


37. Zustandslose Token-Security

Bei zustandsloser Token-Security:


Client sendet Token bei jedem Request
Server validiert Token
Server braucht keine HTTP-Session für Authentifizierung

Beispiel:


Authorization: Bearer eyJhbGciOi...

Üblich für:


REST-APIs
Mobile Apps
Single-Page Applications
Microservices

JWT/Resource Server schauen wir uns später an.

Merksatz:

Zustandslose APIs authentifizieren jeden Request mit einem Token.


38. CSRF

CSRF bedeutet:


Cross-Site Request Forgery

Es ist vor allem wichtig für browserbasierte Session-Authentifizierung.

Einfache Idee:


Eine bösartige Seite bringt den Browser des Benutzers dazu, mit dessen Cookies einen Request an deine Seite zu senden.

Spring Security aktiviert CSRF-Schutz standardmäßig in vielen Web-Szenarien.

Für zustandslose REST-APIs mit Bearer Tokens ist CSRF oft deaktiviert.

Wichtig:


Deaktiviere CSRF nicht blind.
Verstehe zuerst den Authentifizierungsstil.

Merksatz:

CSRF ist besonders wichtig, wenn Browser automatisch Cookies senden.


39. CORS vs. CSRF

CORS und CSRF sind unterschiedlich.

CORS

Frage:


Welche Origins dürfen meine API aus einem Browser aufrufen?

Beispiel:


Erlaube https://app.example.com, https://api.example.com aufzurufen

CSRF

Frage:


Kann eine bösartige Seite den Browser dazu bringen, ungewollte authentifizierte Requests zu senden?

Merksatz:


CORS steuert browserseitigen Cross-Origin-Zugriff.
CSRF schützt vor gefälschten authentifizierten Browser-Requests.

40. Method Security im Überblick

URL-Security:


.requestMatchers("/api/admin/**").hasRole("ADMIN")

Method Security:


@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
}

Method Security schützt Service-Methoden.

Nützlich, wenn:


Autorisierung von Methodenargumenten abhängt
dieselbe Service-Methode von mehreren Controllern aufgerufen wird
Berechtigung auf Business-Ebene nötig ist

Das schauen wir uns später an.

Merksatz:

URL-Security schützt Endpunkte. Method Security schützt Methoden.


41. Security und Service-Schicht

Packe nicht die gesamte Business-Autorisierung nur in URL-Regeln.

Beispiel:


GET /api/clients/10/tasks

URL-Regel kann prüfen:


Benutzer ist authentifiziert
Benutzer hat Rolle USER

Aber Business-Regel muss prüfen:


gehört dieser Benutzer zu Client 10?
hat dieser Benutzer Mandantenzugriff?

Das gehört in Service-/Domain-/Security-Logik.

Merksatz:

URL-Regeln reichen nicht für Berechtigungen auf Datenebene.


42. Gute Security-Schichtung

Ein gutes Design:


SecurityFilterChain -> grundlegende Endpunkt-Zugriffsregeln
Controller -> empfängt authentifizierten Benutzer/Context
Service -> Business-Berechtigungen und Mandantenprüfungen
Repository -> Datenzugriff

Beispiel:


@Transactional(readOnly = true)
public List<TaskDto> findClientTasks(Long clientId, String currentUserEmail) {
UserEntity user = userRepository.findByEmail(currentUserEmail)
.orElseThrow();

if (!permissionService.canAccessClient(user, clientId)) {
throw new AccessDeniedException("No access to client");
}

return taskRepository.findByClientId(clientId)
.stream()
.map(this::toDto)
.toList();
}

Merksatz:

Endpunkt-Security prüft Zugriff auf URLs; Service-Security prüft Zugriff auf Daten und Aktionen.


43. Security-Exceptions

Häufige Security-Ergebnisse:


nicht authentifiziert -> 401
authentifiziert, aber verboten -> 403

Spring Security behandelt viele davon vor den Controllern.

Wichtig:


Security-Exceptions passieren oft in Filtern vor @ControllerAdvice.

Dein MVC-@RestControllerAdvice behandelt daher möglicherweise nicht jeden Security-Fehler.

Spring Security hat seine eigene Exception-Handling-Konfiguration.

Merksatz:

Security-Fehler passieren oft vor dem Controller-Exception-Handling.


44. Typische Überraschung für Einsteiger

Nach dem Hinzufügen des Security-Starters:


implementation("org.springframework.boot:spring-boot-starter-security")

Plötzlich:


GET /api/tasks liefert 401
Browser zeigt Login-Seite
Postman braucht Anmeldedaten
Tests schlagen mit 401/403 fehl
Actuator-Endpunkte können geschützt sein

Warum?


Spring Boot konfiguriert Standard-Security automatisch.

Lösung:


definiere deine eigene SecurityFilterChain
konfiguriere öffentliche und geschützte Endpunkte
konfiguriere Authentifizierungsmethode
passe Tests an

Merksatz:

Der Security-Starter ändert das Endpunkt-Verhalten sofort.


45. Security-Tests im Überblick

Mit Spring Security brauchen Controller-Tests möglicherweise Authentifizierung.

Beispiel:


@WithMockUser(username = "user@example.com", roles = "USER")
@Test
void getsTasks() throws Exception {
mockMvc.perform(get("/api/tasks"))
.andExpect(status().isOk());
}

Ohne Mock-Benutzer:


Test kann 401 zurückgeben

Security-Tests schauen wir uns später an.

Merksatz:

Security betrifft auch Tests.


46. Typische Prüfungsfallen

Falle 1

Spring Security läuft vor den Controllern.


Falle 2

Authentifizierung und Autorisierung sind unterschiedlich.


Authentifizierung = wer bist du?
Autorisierung = was darfst du tun?

Falle 3

401 und 403 sind unterschiedlich.


401 = nicht authentifiziert
403 = authentifiziert, aber nicht berechtigt

Falle 4

Spring Security basiert auf Filterketten.


Falle 5

SecurityFilterChain ist der moderne Weg, Web-Security zu konfigurieren.


Falle 6

HttpSecurity baut die SecurityFilterChain.


Falle 7

Die Reihenfolge der Regeln ist wichtig.

Spezifische Regeln müssen vor allgemeinen Regeln stehen.


Falle 8

hasRole("ADMIN") prüft auf Berechtigung ROLE_ADMIN.


Falle 9

Speichere keine Klartext-Passwörter.

Nutze PasswordEncoder.


Falle 10

HTTP Basic muss in echten Anwendungen HTTPS nutzen.


Falle 11

CSRF und CORS sind unterschiedlich.


Falle 12

Deaktiviere CSRF nicht blind.


Falle 13

Security-Fehler können vor Controller Advice passieren.


Falle 14

URL-Regeln reichen nicht für Mandanten-/Datenebenen-Berechtigungen.


Falle 15

spring-boot-starter-security hinzufügen sichert die App standardmäßig.


47. Echte Prüfungsfrage: Spring Security

Frage:

Welches Problem löst Spring Security?

Antwort:

Spring Security bietet Authentifizierung, Autorisierung und Schutz vor typischen Angriffen für Spring-Anwendungen.


48. Echte Prüfungsfrage: Authentifizierung

Frage:

Was ist Authentifizierung?

Antwort:

Authentifizierung ist der Prozess, zu beweisen, wer der Benutzer ist.


49. Echte Prüfungsfrage: Autorisierung

Frage:

Was ist Autorisierung?

Antwort:

Autorisierung ist der Prozess, zu entscheiden, worauf oder was ein authentifizierter Benutzer zugreifen oder was er tun darf.


50. Echte Prüfungsfrage: Authentifizierung vs. Autorisierung

Frage:

Was ist der Unterschied zwischen Authentifizierung und Autorisierung?

Antwort:

Authentifizierung beantwortet „Wer bist du?“ Autorisierung beantwortet „Was darfst du tun?“


51. Echte Prüfungsfrage: 401 vs. 403

Frage:

Was ist der Unterschied zwischen 401 und 403?

Antwort:

401 bedeutet, der Benutzer ist nicht authentifiziert oder die Authentifizierung ist fehlgeschlagen. 403 bedeutet, der Benutzer ist authentifiziert, darf aber nicht auf die Ressource zugreifen.


52. Echte Prüfungsfrage: Filter

Frage:

Warum nutzt Spring Security Filter?

Antwort:

Filter können HTTP-Requests abfangen, bevor sie die Controller erreichen. Deshalb eignen sie sich für Authentifizierung, Autorisierung, Session-Handling, CSRF und andere Security-Prüfungen.


53. Echte Prüfungsfrage: SecurityFilterChain

Frage:

Was ist SecurityFilterChain?

Antwort:

SecurityFilterChain definiert die Security-Filter und Regeln, die auf passende HTTP-Requests angewendet werden.


54. Echte Prüfungsfrage: HttpSecurity

Frage:

Was ist HttpSecurity?

Antwort:

HttpSecurity ist der Builder zur Konfiguration von Web-Security und zum Erstellen einer SecurityFilterChain.


55. Echte Prüfungsfrage: SecurityContextHolder

Frage:

Was ist SecurityContextHolder?

Antwort:

SecurityContextHolder speichert den aktuellen SecurityContext, der die aktuelle Authentication enthält.


56. Echte Prüfungsfrage: Rollen-Präfix

Frage:

Was prüft hasRole("ADMIN")?

Antwort:

Es prüft auf die Berechtigung ROLE_ADMIN.


57. Echte Prüfungsfrage: PasswordEncoder

Frage:

Wofür wird PasswordEncoder verwendet?

Antwort:

PasswordEncoder wird verwendet, um Passwörter zu hashen und Klartext-Passwörter gegen gespeicherte Passwort-Hashes zu prüfen.


58. Echte Prüfungsfrage: Standard-Security

Frage:

Was passiert, wenn du spring-boot-starter-security zu einer Spring-Boot-Web-App hinzufügst?

Antwort:

Spring Boot konfiguriert Standard-Security automatisch. Üblicherweise werden Endpunkte geschützt, ein Standardbenutzer erstellt und ein generiertes Passwort in den Logs ausgegeben, sofern keine eigene Security-Konfiguration bereitgestellt wird.


59. Interview-Antwort

Frage:

Erkläre den Spring-Security-Request-Ablauf.

Gute Antwort:

In einer Servlet-basierten Spring-Boot-Anwendung durchläuft ein HTTP-Request Spring-Security-Filter, bevor er den DispatcherServlet erreicht. Die Filter können den Benutzer authentifizieren, den SecurityContext befüllen, Autorisierungsregeln prüfen, Security-Exceptions behandeln und den Request dann entweder blockieren oder zum Controller weiterleiten. Wenn der Request abgelehnt wird, wird der Controller nicht aufgerufen.


60. Interview-Antwort

Frage:

Was ist der Unterschied zwischen Authentifizierung und Autorisierung?

Gute Antwort:

Authentifizierung geht darum, die Identität nachzuweisen, zum Beispiel durch Prüfung von Benutzername und Passwort oder Validierung eines JWT. Autorisierung geht um Berechtigungen, zum Beispiel ob der authentifizierte Benutzer die Rolle ADMIN hat oder auf die Daten eines bestimmten Clients zugreifen darf. Authentifizierung passiert in der Regel zuerst, dann Autorisierung.


61. Interview-Antwort

Frage:

Was ist SecurityFilterChain?

Gute Antwort:

SecurityFilterChain ist das zentrale Konfigurationsobjekt für Servlet-Web-Security in modernem Spring Security. Es definiert, welche Security-Filter und Regeln für Requests gelten. Du erstellst es in der Regel als Bean mit HttpSecurity, wo du Autorisierungsregeln, Login-Methode, CSRF, Sessions, HTTP Basic, JWT Resource Server und anderes Security-Verhalten konfigurierst.


62. Interview-Antwort

Frage:

Was ist SecurityContextHolder?

Gute Antwort:

SecurityContextHolder speichert den aktuellen SecurityContext, der die aktuelle Authentication enthält. Die Authentication repräsentiert den authentifizierten Benutzer, inklusive Principal, Credentials und Authorities. Spring Security setzt sie während der Authentifizierung, damit die Anwendung weiß, wer der aktuelle Benutzer ist.


63. Interview-Antwort

Frage:

Warum sollten Passwörter codiert werden?

Gute Antwort:

Passwörter sollten niemals als Klartext gespeichert werden. Ein PasswordEncoder führt eine einwegige Transformation durch, üblicherweise einen sicheren Hash, sodass der gespeicherte Wert nicht das Originalpasswort ist. Beim Login vergleicht Spring Security das Klartext-Passwort mit dem gespeicherten Hash über matches. Das reduziert den Schaden, falls die Datenbank geleakt wird.


64. Interview-Antwort

Frage:

Warum reichen URL-Regeln nicht für echte Autorisierung?

Gute Antwort:

URL-Regeln können groben Zugriff prüfen, zum Beispiel ob Authentifizierung oder eine Admin-Rolle für einen Endpunkt nötig ist. Aber echte Anwendungen brauchen oft Regeln auf Datenebene, zum Beispiel ob ein Benutzer zu einem Mandanten gehört, eine Ressource besitzt oder eine bestimmte Rechnung freigeben darf. Diese Prüfungen gehören in der Regel in die Service- oder Domain-Schicht, manchmal unterstützt durch Method Security.


65. Kleine Code-Übung

Erstelle diese Security-Konfiguration:


@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}

@Bean
UserDetailsService users(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user@example.com")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();

UserDetails admin = User.builder()
.username("admin@example.com")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Fragen:

  1. Wer darf /api/public/hello aufrufen?
  2. Wer darf /api/admin/users aufrufen?
  3. Was bedeutet anyRequest().authenticated()?
  4. Welche Authentifizierungsmethode ist aktiviert?
  5. Warum wird PasswordEncoder gebraucht?

Antworten:

  1. Jeder.
  2. Nur Benutzer mit Rolle ADMIN.
  3. Jeder andere Request erfordert Authentifizierung.
  4. HTTP Basic.
  5. Um codierte Passwort-Hashes zu speichern und zu prüfen.

66. Kleine Bug-Übung 1

Problem:


.requestMatchers("/api/admin/**").hasRole("ROLE_ADMIN")

Frage:

Was ist falsch?

Antwort:

hasRole("ADMIN") prüft automatisch auf Berechtigung ROLE_ADMIN. "ROLE_ADMIN" an hasRole zu übergeben ist meist falsch.

Richtig:


.requestMatchers("/api/admin/**").hasRole("ADMIN")

oder:


.requestMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN")

67. Kleine Bug-Übung 2

Problem:


.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
.requestMatchers("/api/public/**").permitAll()
)

Frage:

Was ist falsch?

Antwort:

Die allgemeine Regel anyRequest().authenticated() steht vor der spezifischen öffentlichen Regel. Spezifische Regeln müssen zuerst kommen.

Richtig:


.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)

68. Kleine Bug-Übung 3

Problem:


@Bean
UserDetailsService users() {
UserDetails user = User.withUsername("user@example.com")
.password("password")
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}

Frage:

Was ist falsch?

Antwort:

Das Passwort ist nicht codiert. Nutze PasswordEncoder.

Richtig:


@Bean
UserDetailsService users(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user@example.com")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}

Übungsfragen und Antworten

Frage 1

Welches Problem löst Spring Security?

Antwort:

Spring Security bietet Authentifizierung, Autorisierung und Schutz vor typischen Angriffen für Spring-Anwendungen.


Frage 2

Was ist Authentifizierung?

Antwort:

Authentifizierung bedeutet, zu beweisen, wer der Benutzer ist.


Frage 3

Was ist Autorisierung?

Antwort:

Autorisierung bedeutet, zu entscheiden, worauf oder was der authentifizierte Benutzer zugreifen oder was er tun darf.


Frage 4

Was ist der Unterschied zwischen Authentifizierung und Autorisierung?

Antwort:

Authentifizierung beantwortet „Wer bist du?“ Autorisierung beantwortet „Was darfst du tun?“


Frage 5

Was ist der Unterschied zwischen 401 und 403?

Antwort:

401 bedeutet, der Benutzer ist nicht authentifiziert oder die Authentifizierung ist fehlgeschlagen. 403 bedeutet, der Benutzer ist authentifiziert, aber nicht berechtigt.


Frage 6

Warum nutzt Spring Security Filter?

Antwort:

Filter können HTTP-Requests abfangen, bevor sie die Controller erreichen. Deshalb eignen sie sich für Authentifizierung, Autorisierung, CSRF, Sessions und andere Security-Prüfungen.


Frage 7

Wo läuft Spring Security im Verhältnis zu Spring MVC?

Antwort:

Spring Security läuft vor den Spring-MVC-Controller-Methoden.


Frage 8

Was ist SecurityFilterChain?

Antwort:

SecurityFilterChain definiert die Security-Filter und Regeln, die für HTTP-Requests gelten.


Frage 9

Was ist HttpSecurity?

Antwort:

HttpSecurity ist der Builder zur Konfiguration von Web-Security und zum Erstellen einer SecurityFilterChain.


Frage 10

Was ist FilterChainProxy?

Antwort:

FilterChainProxy ist die Spring-Security-Komponente, die Requests an die passende Security-Filterkette delegiert.


Frage 11

Was passiert, wenn du spring-boot-starter-security hinzufügst?

Antwort:

Spring Boot konfiguriert Standard-Security automatisch. Üblicherweise werden Endpunkte geschützt, ein Standardbenutzer existiert und ein generiertes Passwort wird in den Logs ausgegeben, sofern keine eigene Konfiguration bereitgestellt wird.


Frage 12

Was macht authorizeHttpRequests?

Antwort:

authorizeHttpRequests konfiguriert URL-basierte Autorisierungsregeln.


Frage 13

Warum ist die Reihenfolge der Regeln wichtig?

Antwort:

Regeln werden in Reihenfolge ausgewertet. Spezifische Regeln müssen vor allgemeinen Regeln wie anyRequest() stehen.


Frage 14

Was prüft hasRole("ADMIN")?

Antwort:

Es prüft, ob der Benutzer die Berechtigung ROLE_ADMIN hat.


Frage 15

Was ist Authentication?

Antwort:

Authentication repräsentiert den aktuell authentifizierten Benutzer, inklusive Principal, Credentials, Authorities und Authentifizierungsstatus.


Frage 16

Was ist SecurityContextHolder?

Antwort:

SecurityContextHolder speichert den aktuellen SecurityContext, der die aktuelle Authentication enthält.


Frage 17

Was ist UserDetailsService?

Antwort:

UserDetailsService lädt Benutzerdetails anhand des Benutzernamens für die Authentifizierung.


Frage 18

Was ist PasswordEncoder?

Antwort:

PasswordEncoder hasht Passwörter und prüft Klartext-Passwörter gegen gespeicherte Passwort-Hashes.


Frage 19

Was ist der Unterschied zwischen session-basierter Security und zustandsloser Token-Security?

Antwort:

Session-basierte Security speichert den Authentifizierungszustand auf dem Server und nutzt ein Session-Cookie. Zustandslose Token-Security sendet bei jedem Request ein Token und verlässt sich nicht auf serverseitige HTTP-Session-Authentifizierung.


Frage 20

Warum reichen URL-Regeln nicht für Berechtigungen auf Datenebene?

Antwort:

URL-Regeln können groben Endpunktzugriff prüfen, aber Berechtigungen auf Datenebene brauchen Business-Prüfungen, zum Beispiel ob ein Benutzer auf einen bestimmten Mandanten, Client, eine Rechnung oder einen Task zugreifen darf.

Merksätze zum Mitnehmen

  • Spring Security schützt Requests vor den Controllern.
  • Authentifizierung bedeutet, die Identität nachzuweisen.
  • Autorisierung bedeutet, Berechtigungen zu prüfen.
  • Authentifizierung = wer bist du?
  • Autorisierung = was darfst du tun?
  • 401 bedeutet nicht authentifiziert.
  • 403 bedeutet authentifiziert, aber nicht berechtigt.
  • Spring Security basiert auf Filterketten.
  • Security-Filter laufen vor dem DispatcherServlet.
  • SecurityFilterChain definiert Security-Filter und Regeln.
  • HttpSecurity baut die SecurityFilterChain.
  • FilterChainProxy delegiert an Security-Filterketten.
  • spring-boot-starter-security hinzufügen sichert die App standardmäßig.
  • authorizeHttpRequests konfiguriert URL-Autorisierung.
  • Die Reihenfolge der Regeln ist wichtig.
  • Spezifische Regeln stehen vor allgemeinen Regeln.
  • hasRole("ADMIN") prüft ROLE_ADMIN.
  • Authentication repräsentiert den aktuell authentifizierten Benutzer.
  • SecurityContextHolder speichert den aktuellen Security Context.
  • UserDetailsService lädt Benutzer.
  • PasswordEncoder speichert und prüft Passwort-Hashes.
  • Speichere keine Klartext-Passwörter.
  • HTTP Basic muss in echten Apps HTTPS nutzen.
  • CSRF und CORS sind unterschiedlich.
  • URL-Regeln reichen nicht für Berechtigungen auf Datenebene.