Woche 6, Tag 1 — Spring Security Mental Model
Ziel
Heute verstehst du das Mental Model von Spring Security.
Die Kernfragen:
- Welches Problem löst Spring Security?
- Was ist Authentifizierung?
- Was ist Autorisierung?
- Was ist der Unterschied zwischen Authentifizierung und Autorisierung?
- Warum nutzt Spring Security Filter?
- Was ist
SecurityFilterChain? - Was ist
FilterChainProxy? - Was ist
SecurityContextHolder? - Was passiert, wenn du
spring-boot-starter-securityhinzufügst? - Wie passt du Security-Regeln an?
- Was ist
PasswordEncoder? - 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.
@Transactionaldefiniert 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
| Konzept | Frage | Beispiel |
|---|---|---|
| Authentifizierung | Wer bist du? | Login mit Benutzername und Passwort |
| Autorisierung | Was 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:
SecurityFilterChaindefiniert, 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
SecurityFilterChainkonfigurierst 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:
HttpSecuritybaut dieSecurityFilterChain.
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:
FilterChainProxyist 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:
FilterChainProxyleitet 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:
authorizeHttpRequestssteuert 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:
| Methode | Bedeutung |
|---|---|
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 BerechtigungROLE_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:
Authenticationrepräsentiert den aktuell authentifizierten Benutzer.
22. SecurityContext
SecurityContext hält die Authentication.
Einfaches Bild:
SecurityContext
└── Authentication
├── principal
├── credentials
└── authorities
Merksatz:
SecurityContextspeichert 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:
SecurityContextHoldergibt 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
AuthenticationoderPrincipalempfangen.
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:
AuthenticationManagerist 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:
AuthenticationManagerentscheidet, 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:
UserDetailsServicelä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:
- Wer darf
/api/public/helloaufrufen? - Wer darf
/api/admin/usersaufrufen? - Was bedeutet
anyRequest().authenticated()? - Welche Authentifizierungsmethode ist aktiviert?
- Warum wird
PasswordEncodergebraucht?
Antworten:
- Jeder.
- Nur Benutzer mit Rolle
ADMIN. - Jeder andere Request erfordert Authentifizierung.
- HTTP Basic.
- 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. SecurityFilterChaindefiniert Security-Filter und Regeln.HttpSecuritybaut dieSecurityFilterChain.FilterChainProxydelegiert an Security-Filterketten.spring-boot-starter-securityhinzufügen sichert die App standardmäßig.authorizeHttpRequestskonfiguriert URL-Autorisierung.- Die Reihenfolge der Regeln ist wichtig.
- Spezifische Regeln stehen vor allgemeinen Regeln.
hasRole("ADMIN")prüftROLE_ADMIN.Authenticationrepräsentiert den aktuell authentifizierten Benutzer.SecurityContextHolderspeichert den aktuellen Security Context.UserDetailsServicelädt Benutzer.PasswordEncoderspeichert 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.