Spring Boot Aufbau-Kurs – Tag 4 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting

🗺️ Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| 1 | Auto-Configuration & Custom Starter | ✅ Abgeschlossen |
| 2 | Spring Data JPA Basics | ✅ Abgeschlossen |
| 3 | JPA Relationships & Queries | ✅ Abgeschlossen |
| → 4 | Spring Security – Part 1 | 👉 DU BIST HIER! |
| 5 | Spring Security – Part 2 | 📜 Kommt als nächstes |
| 6 | Caching & Serialisierung | 🔒 Noch nicht freigeschaltet |
| 7 | Messaging & Email | 🔒 Noch nicht freigeschaltet |
| 8 | Testing & Dokumentation | 🔒 Noch nicht freigeschaltet |
| 9 | Spring Boot Actuator | 🔒 Noch nicht freigeschaltet |
| 10 | Template Engines & Microservices | 🔒 Noch nicht freigeschaltet |
Modul: Spring Boot Aufbau-Kurs (10 Arbeitstage)
Gesamt-Dauer: 10 Arbeitstage
Dein Ziel: Sichere Authentication mit JPA User-Storage implementieren
📋 Voraussetzungen für diesen Tag
Du brauchst:
- ✅ Java JDK 17 oder höher installiert
- ✅ Maven 3.6 + oder in deiner IDE integriert
- ✅ MariaDB oder MySQL läuft lokal
- ✅ Tag 1-3 abgeschlossen (Person Entity mit Repository funktioniert)
- ✅ Grundverständnis von JPA Entities und Relationships
Optional (hilft beim Verständnis):
- Tag 3: JPA Relationships & Queries – OneToMany und ManyToOne Beziehungen
Tag verpasst oder später eingestiegen?
Kein Problem! Dieser Blogbeitrag deckt genau den Stoff von Tag 4 ab.
🔥 Download Starter-Projekt: person-management-day3.zip – damit kannst du direkt loslegen!
⚡ Was du heute baust
Heute machst du deine Person-Management API produktionsreif mit echter Security!
Du baust ein komplettes Authentication-System mit Benutzer-Verwaltung in der Datenbank, sicherer Passwort-Verschlüsselung und einer funktionierenden Login/Logout-Funktion.
Am Ende des Tages:
- Deine API ist durch Spring Security geschützt
- User werden aus der Datenbank geladen
- Passwörter sind mit BCrypt gehascht
- Login und Logout funktionieren über eine Web-Oberfläche
- Du verstehst, wie Spring Security intern arbeitet
🎯 Dein Ziel
Am Ende des Tages kannst du:
- ✅ Spring Security in ein bestehendes Projekt integrieren
- ✅ User-Verwaltung mit JPA Entities erstellen
- ✅ UserDetailsService implementieren für Database-Authentication
- ✅ Passwörter sicher mit BCrypt verschlüsseln
- ✅ SecurityFilterChain konfigurieren
- ✅ Login und Logout Seiten erstellen
- ✅ Die versteckten Security-Features von Spring Security verstehen
⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden
Spring Security schützt deine Anwendung automatisch vor unbefugtem Zugriff. Sobald du die Dependency hinzufügst, sind ALLE Endpoints geschützt – das ist normal und gewollt! Du implementierst einen UserDetailsService, der User aus deiner Datenbank lädt, verschlüsselst Passwörter mit BCrypt, und konfigurierst eine SecurityFilterChain für Login/Logout. Spring Security kümmert sich dann automatisch um Session-Management, CSRF-Protection und viele weitere Security-Features.
Heute lernst du:
- ✅ Spring Security Grundkonzepte (Authentication vs Authorization)
- ✅ UserDetailsService Pattern für Database-basierte User
- ✅ BCrypt Password Encoding für sichere Passwort-Speicherung
- ✅ SecurityFilterChain Konfiguration für Login/Logout
💬 Hi! 👋
Elyndra hier. Heute wird’s ernst – wir bauen echte Security in deine App!
Warum Spring Security? Letzte Woche hatte einer unserer Kursteilnehmer seine API ohne Security deployed. Innerhalb von 2 Stunden hatte jemand alle Daten ausgelesen. Das passiert schneller als du denkst.
Security ist kein „Nice-to-have“ für später – es ist fundamental. Und das Coole: Spring Security macht es dir erstaunlich einfach, wenn du verstehst, wie es funktioniert.
Mein Versprechen: Am Ende des Tages hast du ein komplettes, funktionierendes Security-System. Und du wirst verstehen, was im Hintergrund passiert – keine „Black Box Magic“.
Zeit, das anzugehen! 🔧
🟢 GRUNDLAGEN – Die Basis verstehen
Was ist Spring Security?
Spring Security ist ein mächtiges Framework, das deine Anwendung vor unbefugtem Zugriff schützt. Es kümmert sich um Authentication (Wer bist du?) und Authorization (Was darfst du?).
Metapher:
Stell dir Spring Security wie einen Türsteher in einem Club vor. Der Türsteher prüft:
- Authentication: Bist du auf der Gästeliste? (= Login)
- Authorization: Darfst du in den VIP-Bereich? (= Permissions)
Heute fokussieren wir uns auf Authentication – das Login-System.
Warum brauche ich das?
Problem ohne Spring Security:
- ❌ Jeder kann deine API aufrufen
- ❌ Keine Kontrolle wer was darf
- ❌ Passwörter oft unsicher gespeichert
- ❌ Anfällig für CSRF, XSS und andere Angriffe
Lösung mit Spring Security:
- ✅ Automatischer Schutz aller Endpoints
- ✅ Standardmäßig sichere Defaults
- ✅ Battle-tested gegen bekannte Angriffe
- ✅ Einfache Integration in Spring Boot
Schritt 1: Spring Security Dependency hinzufügen
Datei: pom.xml
<dependencies>
<!-- Existing dependencies -->
<!-- NEU: Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Maven Reload in deiner IDE:
- IntelliJ: Maven Panel → Reload-Icon klicken
- Eclipse: Right-click auf Projekt → Maven → Update Project
- NetBeans: Right-click auf Projekt → Reload POM
Oder via Terminal:
mvn clean install
Was passiert hier?
Du fügst die Spring Security Starter Dependency hinzu. Spring Boot aktiviert automatisch Security mit sicheren Defaults.
Schritt 2: Anwendung neu starten
mvn spring-boot:run
In der Console siehst du:
Using generated security password: 8e6d9f2a-4b1c-4d3e-9a7f-2c8b5e3d1a9c This generated password is for development use only. Your security configuration must be updated before running your application in production.
Was bedeutet das?
Spring Security hat automatisch einen Default-User erstellt:
- Username:
user - Password: Das generierte Passwort aus der Console (bei dir anders!)
Schritt 3: API testen – Der „Schock-Moment“
curl http://localhost:8080/api/persons
Response:
{
"timestamp": "2025-10-24T10:30:00.000+00:00",
"status": 401,
"error": "Unauthorized",
"message": "Full authentication is required to access this resource"
}
🎉 WICHTIGER MOMENT: „Meine API funktioniert nicht mehr! Habe ich was kaputt gemacht?“
NEIN! Das ist ABSOLUT NORMAL! Das passiert jedem beim ersten Mal mit Spring Security!
Was ist passiert?
Spring Security hat automatisch:
- Alle Endpoints geschützt – kein Zugriff ohne Authentication
- Einen Default-User erstellt (
user/ generiertes Passwort) - HTTP Basic Auth aktiviert
- Eine SecurityFilterChain konfiguriert
Das ist Spring Security’s Philosophie: „Deny All by Default“ – sicherer Ansatz!
Schritt 4: Mit Authentication testen
# Username: user # Password: Das generierte Passwort aus deiner Console kopieren! curl -u user:8e6d9f2a-4b1c-4d3e-9a7f-2c8b5e3d1a9c \ http://localhost:8080/api/persons
Response:
[
{
"id": 1,
"firstname": "Max",
"lastname": "Mustermann",
"email": "max@example.com"
}
]
✅ Es funktioniert mit Authentication!
Was macht -u im curl?curl -u username:password URL sendet HTTP Basic Authentication:
Authorization: Basic dXNlcjpwYXNzd29yZA==
Das ist Base64-encoded username:password.
⚠️ Wichtig: HTTP Basic Auth ist nur für Development/Testing! In Production nutzt man Sessions oder JWT (kommt morgen in Tag 5).
✅ Checkpoint: Funktioniert es bis hier?
- [ ] Spring Security Dependency ist hinzugefügt
- [ ] Anwendung startet und zeigt generiertes Passwort
- [ ] API-Aufruf ohne Authentication gibt 401 Unauthorized
- [ ] API-Aufruf MIT Authentication funktioniert
Probleme?
➡️ Passwort aus Console kopiert? Achte auf Whitespaces!
➡️ Maven Dependencies geladen? Reload in IDE durchführen!
➡️ Anwendung wirklich neu gestartet? Bei jedem Start neues Passwort!
Schritt 5: User Entity erstellen
Jetzt erstellen wir eine User-Entity für die Datenbank statt dem generierten Passwort!
Datei: src/main/java/com/javafleet/personmanagement/entity/User.java
package com.javafleet.personmanagement.entity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Username darf nicht leer sein")
@Size(min = 3, max = 50)
@Column(unique = true, nullable = false, length = 50)
private String username;
@NotBlank(message = "Password darf nicht leer sein")
@Column(nullable = false)
private String password; // Wird encrypted gespeichert!
@Enumerated(EnumType.STRING)
@Column(length = 20, nullable = false)
private Role role = Role.USER;
@Column(nullable = false)
private boolean enabled = true;
@Column(nullable = false)
private boolean accountNonLocked = true;
}
Was passiert hier?
@Entity: JPA Entity für Datenbank-Tabelle „users“username: Eindeutiger Username (unique constraint)password: Wird encrypted gespeichert (nie plain text!)role: Enum für User-Rollen (USER, ADMIN, etc.)enabled: Ist der Account aktiviert?accountNonLocked: Ist der Account gesperrt?
Datei: src/main/java/com/javafleet/personmanagement/entity/Role.java
package com.javafleet.personmanagement.entity;
public enum Role {
USER,
ADMIN,
MODERATOR
}
Was ist das?
Ein Enum für Benutzer-Rollen. Morgen in Tag 5 nutzen wir das für Authorization!
Schritt 6: UserRepository erstellen
Datei: src/main/java/com/javafleet/personmanagement/repository/UserRepository.java
package com.javafleet.personmanagement.repository;
import com.javafleet.personmanagement.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
}
Was passiert hier?
findByUsername: Spring Data JPA erstellt automatisch die Query:SELECT * FROM users WHERE username = ?Optional<User>: User kann existieren oder nicht (null-safe)existsByUsername: Prüft ob Username bereits existiert
✅ Checkpoint: Entity und Repository erstellt?
- [ ] User.java Entity mit allen Feldern erstellt
- [ ] Role.java Enum erstellt
- [ ] UserRepository.java Interface erstellt
- [ ] Keine Compile-Fehler
Probleme?
➡️ Lombok funktioniert nicht? @Data braucht Lombok-Plugin in IDE!
➡️ JPA-Imports fehlen? jakarta.persistence.* statt javax.persistence.* (seit Spring Boot 3)!
Schritt 7: UserDetailsService implementieren
Der UserDetailsService lädt User aus der Datenbank für Spring Security.
Datei: src/main/java/com/javafleet/personmanagement/security/CustomUserDetailsService.java
package com.javafleet.personmanagement.security;
import com.javafleet.personmanagement.entity.User;
import com.javafleet.personmanagement.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("Loading user: {}", username);
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.isEnabled(),
true, // accountNonExpired
true, // credentialsNonExpired
user.isAccountNonLocked(),
getAuthorities(user)
);
}
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
return List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole().name()));
}
}
Was passiert hier?
Zeile für Zeile erklärt:
@Service: Spring Component – wird automatisch als Bean erstelltimplements UserDetailsService: Spring Security Interface – wird automatisch gefundenloadUserByUsername(): Spring Security ruft diese Methode beim Login aufuserRepository.findByUsername(): User aus Datenbank laden.orElseThrow(): Wenn User nicht existiert → UsernameNotFoundExceptionnew User(...): Spring Security’s User-Objekt erstellen (NICHT unsere Entity!)getAuthorities(): User-Rolle in Spring Security Format konvertieren
Wichtig: Spring Security hat eine eigene User-Klasse! Wir erstellen ein Spring Security UserDetails Objekt aus unserer Entity.
Schritt 8: PasswordEncoder konfigurieren
Passwörter dürfen NIEMALS plain text gespeichert werden! Wir nutzen BCrypt.
Datei: src/main/java/com/javafleet/personmanagement/config/SecurityConfig.java
package com.javafleet.personmanagement.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.permitAll()
)
.httpBasic(basic -> {});
return http.build();
}
}
Was passiert hier?
PasswordEncoder Bean:
BCryptPasswordEncoder: Hasht Passwörter mit BCrypt-Algorithmus- BCrypt ist langsam by design – schützt vor Brute-Force
- Jedes Passwort bekommt einen Salt – gleiche Passwörter haben unterschiedliche Hashes
SecurityFilterChain Bean:
.authorizeHttpRequests(): Alle Requests brauchen Authentication.formLogin(): Aktiviert Login-Seite unter/login.logout(): Aktiviert Logout-Funktion.httpBasic(): Erlaubt HTTP Basic Auth für API-Tests
Schritt 9: Test-User in Datenbank erstellen
Wir erstellen einen Admin-User mit verschlüsseltem Passwort.
Datei: src/main/resources/data.sql
-- Test Users für Development
-- WICHTIG: Passwörter sind mit BCrypt verschlüsselt!
-- User: admin, Password: admin123
INSERT INTO users (username, password, role, enabled, account_non_locked)
VALUES ('admin',
'$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
'ADMIN',
true,
true);
-- User: user, Password: user123
INSERT INTO users (username, password, role, enabled, account_non_locked)
VALUES ('user',
'$2a$10$VEjxo0jq2YT4jYVd1.OB7uONEp0eVGvGg3oMABpRNKF5m2q5F8fMm',
'USER',
true,
true);
Was ist das für ein Password-Hash?$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
Aufbau eines BCrypt-Hashes:
$2a$: BCrypt Version10$: Cost Factor (2^10 = 1024 Iterationen)- Rest: Salt + Hash
Wie erstelle ich eigene Hashes?
Du kannst online BCrypt-Generatoren nutzen oder in Java:
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String hash = encoder.encode("meinPasswort");
System.out.println(hash);
✅ Checkpoint: Security konfiguriert?
- [ ] CustomUserDetailsService erstellt und als @Service annotiert
- [ ] SecurityConfig mit PasswordEncoder Bean
- [ ] SecurityFilterChain konfiguriert
- [ ] Test-User in data.sql eingefügt
- [ ] Anwendung startet ohne Fehler
Probleme?
➡️ „No qualifying bean of type PasswordEncoder“? @Bean auf passwordEncoder() vergessen!
➡️ SQL-Fehler beim Start? Tabelle „users“ bereits vorhanden? Prüfe spring.jpa.hibernate.ddl-auto in application.properties!
➡️ UserDetailsService nicht gefunden? @Service Annotation vergessen!
Schritt 10: Login testen
1. Anwendung neu starten:
mvn spring-boot:run
2. In der Console:
Kein "Using generated security password" mehr!
Spring Security nutzt jetzt deine Datenbank-User!
3. Browser öffnen:
http://localhost:8080
Du wirst automatisch zu /login umgeleitet – aber: 404 Error!
Warum? Wir haben noch keine Login-Page erstellt. Spring Security’s Default-Login funktioniert nur wenn Thymeleaf im Classpath ist.
Schritt 11: Thymeleaf und Login-Page hinzufügen
Datei: pom.xml
<!-- Thymeleaf für Web-Templates -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Maven Reload durchführen!
Datei: src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Person Management</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-container {
background: white;
padding: 2.5rem;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 400px;
}
h1 {
color: #333;
margin-bottom: 0.5rem;
font-size: 1.8rem;
}
.subtitle {
color: #666;
margin-bottom: 2rem;
font-size: 0.9rem;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
color: #333;
font-weight: 500;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 0.75rem;
border: 2px solid #e1e8ed;
border-radius: 5px;
font-size: 1rem;
transition: border-color 0.3s;
}
input[type="text"]:focus,
input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
.remember-me {
display: flex;
align-items: center;
margin-bottom: 1.5rem;
}
.remember-me input {
margin-right: 0.5rem;
}
button {
width: 100%;
padding: 0.75rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 5px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
.error-message {
background-color: #fee;
border-left: 4px solid #f44;
padding: 1rem;
margin-bottom: 1.5rem;
border-radius: 4px;
color: #c33;
}
.logout-message {
background-color: #efe;
border-left: 4px solid #4a4;
padding: 1rem;
margin-bottom: 1.5rem;
border-radius: 4px;
color: #363;
}
</style>
</head>
<body>
<div class="login-container">
<h1>🔐 Person Management</h1>
<p class="subtitle">Bitte melde dich an um fortzufahren</p>
<div th:if="${param.error}" class="error-message">
❌ Ungültige Anmeldedaten. Bitte versuche es erneut.
</div>
<div th:if="${param.logout}" class="logout-message">
✅ Du wurdest erfolgreich abgemeldet.
</div>
<form th:action="@{/login}" method="post">
<div class="form-group">
<label for="username">Benutzername:</label>
<input type="text"
id="username"
name="username"
required
autofocus
placeholder="admin">
</div>
<div class="form-group">
<label for="password">Passwort:</label>
<input type="password"
id="password"
name="password"
required
placeholder="********">
</div>
<div class="remember-me">
<input type="checkbox" id="remember-me" name="remember-me">
<label for="remember-me" style="margin-bottom: 0; font-weight: normal;">
Angemeldet bleiben (30 Tage)
</label>
</div>
<button type="submit">Anmelden</button>
</form>
<div style="margin-top: 1.5rem; text-align: center; color: #666; font-size: 0.85rem;">
<p><strong>Test-Zugänge:</strong></p>
<p>Admin: admin / admin123</p>
<p>User: user / user123</p>
</div>
</div>
</body>
</html>
Was macht diese HTML-Seite?
- Modern gestyltes Login-Formular
- Zeigt Fehler-Meldungen bei falschem Passwort
- Zeigt Erfolgs-Meldung nach Logout
- „Remember Me“ Checkbox für persistente Sessions
- Responsive Design für Mobile
Datei: src/main/resources/templates/persons.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Persons - Verwaltung</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #667eea;
}
h1 {
color: #333;
}
.logout-form {
display: inline;
}
.logout-button {
background-color: #f44336;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
}
.logout-button:hover {
background-color: #da190b;
}
.welcome {
color: #666;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #667eea;
color: white;
}
tr:hover {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div class="header">
<div>
<h1>👥 Person Management System</h1>
<p class="welcome">
Willkommen, <strong th:text="${#authentication.name}">User</strong>!
Rolle: <strong th:text="${#authentication.authorities}">ROLE</strong>
</p>
</div>
<form th:action="@{/logout}" method="post" class="logout-form">
<button type="submit" class="logout-button">Abmelden</button>
</form>
</div>
<h2>Alle Personen</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Vorname</th>
<th>Nachname</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Max</td>
<td>Mustermann</td>
<td>max@example.com</td>
</tr>
<tr>
<td>2</td>
<td>Erika</td>
<td>Musterfrau</td>
<td>erika@example.com</td>
</tr>
</tbody>
</table>
<p style="margin-top: 30px; color: #666;">
📝 Hinweis: Dies ist eine statische Demo-Seite. Morgen in Tag 5 verbinden wir das mit der echten API!
</p>
</body>
</html>
Datei: src/main/java/com/javafleet/personmanagement/controller/WebController.java
package com.javafleet.personmanagement.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class WebController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/persons")
public String persons() {
return "persons";
}
@GetMapping("/")
public String home() {
return "redirect:/persons";
}
}
Was macht der Controller?
/login: Zeigt Login-Seite/persons: Zeigt Persons-Übersicht (geschützt!)/: Root-URL leitet zu /persons um
Schritt 12: SecurityConfig erweitern für Remember-Me
Datei: SecurityConfig.java aktualisieren:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/persons", true)
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.rememberMe(remember -> remember
.key("mySecretRememberMeKey")
.tokenValiditySeconds(86400 * 30) // 30 Tage
.rememberMeParameter("remember-me")
)
.httpBasic(basic -> {});
return http.build();
}
Was ist neu?
.defaultSuccessUrl("/persons", true): Nach Login → /persons.rememberMe(): Aktiviert Remember-Me FunktionalitättokenValiditySeconds(86400 * 30): Cookie bleibt 30 Tage gültig
✅ Checkpoint: Login-System komplett?
- [ ] Thymeleaf Dependency hinzugefügt
- [ ] login.html und persons.html erstellt
- [ ] WebController erstellt
- [ ] SecurityConfig mit Remember-Me erweitert
- [ ] Anwendung startet ohne Fehler
Schritt 13: Komplettes System testen!
1. Anwendung starten:
mvn spring-boot:run
2. Browser öffnen:
http://localhost:8080
3. Automatischer Redirect zu Login:
Du wirst zu /login umgeleitet – das ist korrekt! Du bist nicht eingeloggt.
4. Mit admin einloggen:
- Benutzername:
admin - Passwort:
admin123 - Optional: „Angemeldet bleiben“ aktivieren
5. Nach erfolgreichem Login:
Redirect zu → http://localhost:8080/persons
Du siehst:
- Willkommens-Nachricht mit deinem Username
- Deine Rolle (ADMIN)
- Personen-Tabelle
- Logout-Button
6. Logout testen:
Klicke auf „Abmelden“ → Redirect zu /login?logout mit Erfolgs-Meldung
7. Remember-Me testen:
- Einloggen mit aktivierter „Angemeldet bleiben“ Checkbox
- Browser schließen
- Browser neu öffnen und
http://localhost:8080aufrufen - Du bist noch eingeloggt! 🎉
✅ Checkpoint: Funktioniert alles?
- [ ] Login-Seite wird angezeigt
- [ ] Einloggen mit admin/admin123 funktioniert
- [ ] Nach Login sehe ich /persons Seite
- [ ] Mein Username und Rolle werden angezeigt
- [ ] Logout funktioniert
- [ ] Remember-Me funktioniert (nach Browser-Neustart noch eingeloggt)
Probleme?
➡️ 404 auf /login? WebController erstellt? @GetMapping("/login") vorhanden?
➡️ „Forbidden“ Fehler? CSRF-Token fehlt? (Sollte nicht passieren mit form-based login)
➡️ Passwort falsch? BCrypt-Hash in data.sql korrekt kopiert?
➡️ Remember-Me funktioniert nicht? Cookie im Browser deaktiviert?
🟡 PROFESSIONALS – Production-Ready Skills
Jetzt wird’s ernst! Du verstehst die Grundlagen – Zeit die „Magie“ hinter Spring Security zu verstehen.
Die SecurityFilterChain verstehen
Was ist eine FilterChain?
Jeder HTTP-Request durchläuft eine Kette von Security-Filtern:
HTTP Request
↓
[1] SecurityContextPersistenceFilter
↓
[2] LogoutFilter
↓
[3] UsernamePasswordAuthenticationFilter
↓
[4] RememberMeAuthenticationFilter
↓
[5] AnonymousAuthenticationFilter
↓
[6] ExceptionTranslationFilter
↓
[7] FilterSecurityInterceptor
↓
Your Controller
Was macht jeder Filter?
- SecurityContextPersistenceFilter: Lädt Authentication aus Session
- LogoutFilter: Erkennt Logout-Requests
- UsernamePasswordAuthenticationFilter: Verarbeitet Login-Form
- RememberMeAuthenticationFilter: Prüft Remember-Me Cookie
- AnonymousAuthenticationFilter: Erstellt Anonymous-User wenn nicht eingeloggt
- ExceptionTranslationFilter: Fängt Security-Exceptions ab
- FilterSecurityInterceptor: Prüft ob User Zugriff hat
Was passiert beim Login? (Schritt für Schritt)
1. User öffnet http://localhost:8080/persons
FilterChain prüft: Ist User authenticated? → Nein! → ExceptionTranslationFilter fängt ab → Redirect zu /login
2. User gibt Username/Password ein und klickt „Anmelden“
POST /login
↓
UsernamePasswordAuthenticationFilter
↓
AuthenticationManager
↓
UserDetailsService.loadUserByUsername("admin")
↓
UserRepository.findByUsername("admin")
↓
User aus DB geladen
↓
PasswordEncoder.matches(eingabe, dbHash)
↓
BCrypt vergleicht Passwörter
↓
✅ Match!
↓
Authentication-Objekt erstellt
↓
SecurityContext.setAuthentication(auth)
↓
Session erstellt mit JSESSIONID Cookie
↓
Redirect zu /persons
3. User ist jetzt eingeloggt:
Jeder weitere Request:
HTTP Request + JSESSIONID Cookie
↓
SecurityContextPersistenceFilter
↓
Lädt Authentication aus Session
↓
SecurityContext ist jetzt gefüllt
↓
Controller kann @AuthenticationPrincipal nutzen
SecurityContext und ThreadLocal
Was ist SecurityContext?
Der SecurityContext speichert das Authentication-Objekt des aktuellen Users.
// SecurityContext abrufen SecurityContext context = SecurityContextHolder.getContext(); Authentication auth = context.getAuthentication(); String username = auth.getName(); Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
Wichtig: SecurityContext ist ThreadLocal!
Thread 1 (User A) → SecurityContext mit Auth von User A Thread 2 (User B) → SecurityContext mit Auth von User B Thread 3 (User C) → SecurityContext mit Auth von User C
Jeder Thread hat seinen eigenen SecurityContext – keine Konflikte zwischen parallelen Requests!
Die versteckten Security-Features
Spring Security macht im Hintergrund viel mehr als nur Login/Logout!
1. CSRF Protection (Cross-Site Request Forgery)
// Automatisch aktiviert für form-based Login! // CSRF-Token wird in jedes Formular eingefügt
Was schützt das?
- Verhindert dass externe Websites Requests in deinem Namen senden
- Beispiel: Böse Website mit
<form action="http://deine-app.com/delete-account">
Warum funktioniert unser Login trotzdem?
Thymeleaf fügt automatisch CSRF-Token in Forms ein: th:action="@{/login}"
2. Security Headers
X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000
Was schützen diese Header?
- X-Content-Type-Options: Verhindert MIME-Type Sniffing
- X-Frame-Options: Verhindert Clickjacking (deine Seite in iframe)
- X-XSS-Protection: Schützt vor Cross-Site Scripting
- Strict-Transport-Security: Erzwingt HTTPS
3. Session Fixation Protection
// Automatisch aktiviert! // Nach Login wird neue Session-ID erstellt
Was schützt das?
Verhindert dass Angreifer deine Session-ID stehlen und wiederverwenden.
Vor Login:
JSESSIONID: ABC123
Nach Login:
JSESSIONID: XYZ789 (neue ID!)
4. Concurrent Session Control
// In SecurityConfig aktivierbar:
.sessionManagement(session -> session
.maximumSessions(1) // Nur 1 Session pro User
.maxSessionsPreventsLogin(true) // Neuere Session blockiert ältere
)
Best Practice: Custom Login Success Handler
Manchmal willst du nach dem Login zusätzliche Aktionen ausführen:
Datei: src/main/java/com/javafleet/personmanagement/security/CustomAuthenticationSuccessHandler.java
package com.javafleet.personmanagement.security;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.LocalDateTime;
@Component
@Slf4j
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
String username = authentication.getName();
String ip = request.getRemoteAddr();
log.info("Successful login: User '{}' from IP {} at {}",
username, ip, LocalDateTime.now());
// Hier könntest du:
// - Login in Audit-Log speichern
// - Last-Login-Timestamp updaten
// - Failed-Login-Counter zurücksetzen
// Redirect zu Standard-Success-URL
response.sendRedirect("/persons");
}
}
In SecurityConfig einbinden:
@RequiredArgsConstructor // Lombok - erstellt Constructor
public class SecurityConfig {
private final CustomAuthenticationSuccessHandler successHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.formLogin(form -> form
.loginPage("/login")
.successHandler(successHandler) // Custom Handler!
.permitAll()
)
// ... rest der Config
}
}
✅ Checkpoint: Professionals verstanden?
- [ ] Du verstehst die SecurityFilterChain und ihre Filter
- [ ] Du weißt was beim Login intern passiert
- [ ] Du kennst SecurityContext und ThreadLocal
- [ ] Du weißt welche versteckten Security-Features aktiv sind
- [ ] CSRF, Security Headers, Session Fixation sind klar
Alles klar? Du bist jetzt ein Spring Security Professional! 🚀
🔵 BONUS – Für die Neugierigen
Du hast die Basics drauf und bist Production-Ready?
Perfekt! Hier ist noch mehr, wenn du tiefer eintauchen willst.
⚠️ Optional! Überspringe diesen Teil, wenn dir der Kopf raucht.
Du kannst später zurückkommen.
Bonus 1: Account Locking nach fehlgeschlagenen Logins
Datei: User.java erweitern:
@Entity
@Table(name = "users")
@Data
public class User {
// ... existing fields
@Column(nullable = false)
private int failedLoginAttempts = 0;
@Column
private LocalDateTime lastFailedLogin;
@Column
private LocalDateTime lockedUntil;
}
Datei: CustomAuthenticationFailureHandler.java erstellen:
package com.javafleet.personmanagement.security;
import com.javafleet.personmanagement.entity.User;
import com.javafleet.personmanagement.repository.UserRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.LocalDateTime;
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final UserRepository userRepository;
private static final int MAX_FAILED_ATTEMPTS = 3;
private static final int LOCK_TIME_MINUTES = 15;
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
String username = request.getParameter("username");
User user = userRepository.findByUsername(username).orElse(null);
if (user != null) {
if (user.isAccountNonLocked() && user.isEnabled()) {
// Increment failed attempts
int attempts = user.getFailedLoginAttempts() + 1;
user.setFailedLoginAttempts(attempts);
user.setLastFailedLogin(LocalDateTime.now());
if (attempts >= MAX_FAILED_ATTEMPTS) {
// Lock account
user.setAccountNonLocked(false);
user.setLockedUntil(LocalDateTime.now().plusMinutes(LOCK_TIME_MINUTES));
log.warn("Account locked: {} after {} failed attempts",
username, attempts);
}
userRepository.save(user);
}
}
String errorMessage = "Invalid username or password";
if (exception instanceof LockedException) {
errorMessage = "Account is locked. Try again in 15 minutes.";
}
response.sendRedirect("/login?error&message=" + errorMessage);
}
}
Login-Page erweitern um Fehlermeldung:
<div th:if="${param.error}" class="error-message">
❌ <span th:text="${param.message != null ? param.message[0] : 'Ungültige Anmeldedaten'}">
Ungültige Anmeldedaten
</span>
</div>
Bonus 2: Password Strength Validation
Datei: PasswordStrengthValidator.java erstellen:
package com.javafleet.personmanagement.validation;
import org.springframework.stereotype.Component;
import java.util.regex.Pattern;
@Component
public class PasswordStrengthValidator {
private static final int MIN_LENGTH = 8;
private static final Pattern UPPERCASE = Pattern.compile("[A-Z]");
private static final Pattern LOWERCASE = Pattern.compile("[a-z]");
private static final Pattern DIGIT = Pattern.compile("[0-9]");
private static final Pattern SPECIAL_CHAR = Pattern.compile("[!@#$%^&*(),.?\":{}|<>]");
public ValidationResult validate(String password) {
ValidationResult result = new ValidationResult();
if (password == null || password.length() < MIN_LENGTH) {
result.addError("Passwort muss mindestens " + MIN_LENGTH + " Zeichen lang sein");
}
if (!UPPERCASE.matcher(password).find()) {
result.addError("Passwort muss mindestens einen Großbuchstaben enthalten");
}
if (!LOWERCASE.matcher(password).find()) {
result.addError("Passwort muss mindestens einen Kleinbuchstaben enthalten");
}
if (!DIGIT.matcher(password).find()) {
result.addError("Passwort muss mindestens eine Ziffer enthalten");
}
if (!SPECIAL_CHAR.matcher(password).find()) {
result.addError("Passwort muss mindestens ein Sonderzeichen enthalten");
}
return result;
}
public static class ValidationResult {
private final java.util.List<String> errors = new java.util.ArrayList<>();
public void addError(String error) {
errors.add(error);
}
public boolean isValid() {
return errors.isEmpty();
}
public java.util.List<String> getErrors() {
return errors;
}
}
}
Bonus 3: Audit Logging für Security-Events
Datei: SecurityAuditLog.java Entity:
package com.javafleet.personmanagement.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Entity
@Table(name = "security_audit_log")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SecurityAuditLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String eventType; // LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT, etc.
@Column
private String ipAddress;
@Column
private String userAgent;
@Column(nullable = false)
private LocalDateTime timestamp;
@Column(columnDefinition = "TEXT")
private String details;
}
Datei: SecurityAuditService.java:
package com.javafleet.personmanagement.service;
import com.javafleet.personmanagement.entity.SecurityAuditLog;
import com.javafleet.personmanagement.repository.SecurityAuditLogRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
@Slf4j
public class SecurityAuditService {
private final SecurityAuditLogRepository auditLogRepository;
public void logLoginSuccess(String username, HttpServletRequest request) {
SecurityAuditLog log = new SecurityAuditLog();
log.setUsername(username);
log.setEventType("LOGIN_SUCCESS");
log.setIpAddress(getClientIP(request));
log.setUserAgent(request.getHeader("User-Agent"));
log.setTimestamp(LocalDateTime.now());
auditLogRepository.save(log);
log.info("Audit: LOGIN_SUCCESS for user '{}' from IP {}", username, log.getIpAddress());
}
public void logLoginFailure(String username, HttpServletRequest request, String reason) {
SecurityAuditLog log = new SecurityAuditLog();
log.setUsername(username != null ? username : "UNKNOWN");
log.setEventType("LOGIN_FAILURE");
log.setIpAddress(getClientIP(request));
log.setUserAgent(request.getHeader("User-Agent"));
log.setTimestamp(LocalDateTime.now());
log.setDetails(reason);
auditLogRepository.save(log);
log.warn("Audit: LOGIN_FAILURE for user '{}' from IP {}: {}",
username, log.getIpAddress(), reason);
}
public void logLogout(String username, HttpServletRequest request) {
SecurityAuditLog log = new SecurityAuditLog();
log.setUsername(username);
log.setEventType("LOGOUT");
log.setIpAddress(getClientIP(request));
log.setUserAgent(request.getHeader("User-Agent"));
log.setTimestamp(LocalDateTime.now());
auditLogRepository.save(log);
log.info("Audit: LOGOUT for user '{}' from IP {}", username, log.getIpAddress());
}
private String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null) {
return request.getRemoteAddr();
}
return xfHeader.split(",")[0];
}
}
✅ Bonus Checkpoint: Was hast du gebaut?
- [ ] Account Locking nach 3 fehlgeschlagenen Logins
- [ ] Password Strength Validation
- [ ] Audit Logging für alle Security-Events
Glückwunsch! Das ist Enterprise-Level Security! 🏆
✅ Checkpoint: Hast du Tag 4 geschafft?
Kontrolliere deine Erfolge:
🟢 Grundlagen (PFLICHT):
- [ ] Spring Security Dependency hinzugefügt
- [ ] „401 Unauthorized“ beim ersten Start verstanden
- [ ] User Entity mit encrypted Passwörtern erstellt
- [ ] UserDetailsService implementiert
- [ ] PasswordEncoder (BCrypt) konfiguriert
- [ ] SecurityFilterChain konfiguriert
- [ ] Login und Logout funktionieren
- [ ] Test-User in Datenbank eingefügt
🟡 Professionals (EMPFOHLEN):
- [ ] SecurityFilterChain im Detail verstanden
- [ ] Einzelne Filter und ihre Aufgaben kennst
- [ ] SecurityContext und ThreadLocal verstanden
- [ ] Versteckte Security-Features kennst (CSRF, Headers, Session Fixation)
- [ ] Remember-Me Funktionalität funktioniert
- [ ] Custom Success Handler erstellt (optional)
🔵 Bonus (OPTIONAL):
- [ ] Account Locking nach fehlgeschlagenen Logins
- [ ] Password Strength Validation
- [ ] Audit Logging für Security-Events
✅ Alle Grundlagen-Häkchen gesetzt?
Glückwunsch! Du bist bereit für Tag 5! 🎉
❌ Nicht alles funktioniert?
Kein Problem! Schau nochmal in:
- Die Troubleshooting-Tipps bei den Checkpoints
- Das FAQ weiter unten
- Das Download-Projekt zum Vergleichen
Brauchst du mehr Zeit?
Nimm dir die Zeit! Security ist fundamental – hier sollte alles sitzen. 💪
❓ FAQ (Häufige Fragen)
Q: Warum bekomme ich immer noch 401 Unauthorized nach dem Login?
A: Überprüfe ob dein UserDetailsService als @Service annotiert ist und ob Spring ihn findet. Schau in die Logs: Wird loadUserByUsername() aufgerufen?
Q: Kann ich plain text Passwörter für Testing nutzen?
A: Technisch ja mit {noop}password123 im Passwort-Feld, aber besser nicht! BCrypt ist fast genug für Tests und trainiert dich für Production.
Q: Was ist der Unterschied zwischen Authentication und Authorization?
A: Authentication = „Wer bist du?“ (Login). Authorization = „Was darfst du?“ (Permissions). Authorization kommt morgen in Tag 5!
Q: Warum sollte ich CSRF für APIs disabled lassen?
A: REST APIs sind stateless (keine Session/Cookies), CSRF betrifft nur Cookie-based Authentication. Für APIs nutzt man JWT (morgen in Tag 5!).
Q: Ist BCrypt sicher genug?
A: Ja! BCrypt ist seit 1999 bewährt. Mit Cost-Factor 10-12 ist es sicher gegen moderne Hardware-Angriffe. Jedes Passwort bekommt automatisch einen eigenen Salt.
Q: Kann ich mehrere Roles pro User haben?
A: Ja! Morgen in Tag 5 bauen wir das mit User-Role Relationships (ManyToMany). Heute lernen wir die Grundlagen mit einem Role pro User.
Q: Wie geht ihr im Team mit Stress bei Security-Problemen um? 🤔
A: Gute Frage… Manchmal gibt’s im Team mehr als nur Code-Probleme. Solche Geschichten gehören zu Herz Schmerz und private logs. Aber das ist ein anderes Kapitel. 📖
Q: Was passiert wenn die Datenbank beim Login down ist?
A: loadUserByUsername() wirft Exception → Spring Security fängt sie → „Internal Server Error“ wird gezeigt. In Production: Proper Error-Handling mit Custom Exception Handler!
💬 Real Talk: Learning Friday
Java Fleet Office, Freitag 16:00 Uhr. Das Team trifft sich zum Wochenabschluss.
Nova macht Notizen auf ihrem Laptop, Elyndra hat Kekse mitgebracht.
Franz-Martin: „So, Leute – was habt ihr diese Woche bei Spring Security gelernt?“
Nova: „Ich hab endlich verstanden warum meine API plötzlich 401 zurückgibt! Das war so ein Aha-Moment. Spring Security schützt automatisch alles – das ist kein Bug, das ist ein Feature!“
Elyndra: „Genau! Und weißt du was? Ich hab jahrelang Security genutzt ohne zu verstehen was im Hintergrund passiert. Diese Filter-Chain – mind blown!“
Code Sentinel: „Der häufigste Fehler bei Anfängern: Sie vergessen @Service auf dem UserDetailsService. Dann wundert sich Spring Security und findet keine User.“
Nova: „Ja! Den Fehler hab ich auch gemacht! Aber jetzt weiß ich, dass Spring automatisch nach einem UserDetailsService-Bean sucht.“
Kofi: „Was ich cool finde: BCrypt macht automatisch Salting. Du musst dir nicht selbst einen Salt ausdenken – das war früher echt nervig.“
Elyndra: „Und das Beste: Die versteckten Features! CSRF-Protection, Security Headers, Session Fixation Prevention – läuft alles im Hintergrund.“
Franz-Martin (lächelt): „Genau deshalb machen wir Learning Friday. Security sieht kompliziert aus, aber wenn man die Basics versteht, ist es eigentlich logisch.“
Nova: „Für nächste Woche steht Authorization an, oder? Ich bin schon gespannt auf @PreAuthorize!“
Elyndra: „Oh ja, Method Security ist richtig mächtig. Da wird’s interessant!“
🗺️ Deine nächsten Schritte
✅ Du hast Tag 4 geschafft! Was jetzt?
Nächster Tag:
- 📜 Tag 5: Spring Security – Part 2: Authorization & Method Security
- 📅 Erscheint: Morgen
- ⏱️ Dauer: 8 Stunden
Was du morgen lernst:
- Role-Based Access Control (RBAC)
- URL-basierte Authorization (hasRole, hasAuthority)
- Method Security mit @PreAuthorize und @PostAuthorize
- SpEL Expressions für komplexe Zugriffsregeln
- Custom Security Expressions
- Bonus: JWT statt Session-based Authentication
Vorbereitung für Tag 5:
- [ ] Tag 4 Checkpoint vollständig ✅
- [ ] Login-System funktioniert
- [ ] User mit verschiedenen Rollen in Datenbank
- [ ] SecurityConfig verstanden
Noch nicht bereit?
Kein Problem! Arbeite Tag 4 nochmal nach. Security ist fundamental – hier sollte alles sitzen! 💪
📥 Download & Ressourcen
Projekt zum Download:
👉 Tag-4-Spring-Boot-Aufbau-Security-Part1.zip (Stand: 24.10.2025)
Was ist im ZIP enthalten:
- ✅ Komplettes Maven-Projekt mit Spring Security
- ✅ User Entity mit BCrypt Passwörtern
- ✅ UserDetailsService Implementation
- ✅ SecurityFilterChain Konfiguration
- ✅ Login & Persons HTML-Seiten (Thymeleaf)
- ✅ Test-User SQL-Script (data.sql)
- ✅ README.md mit Setup-Anleitung
- ✅ cURL-Testskript für API-Tests
Projekt starten:
# ZIP entpacken cd Tag-4-Spring-Boot-Aufbau-Security-Part1 mvn spring-boot:run # Browser öffnen http://localhost:8080 # Einloggen mit: Username: admin Password: admin123
Weiterführend:
- 📚 Spring Security Reference Documentation – Offizielle Docs
- 🎥 Spring Security Architecture – Deep Dive
- 💬 Spring Security auf Stack Overflow – Community Support
Probleme? Schreib mir: elyndra@java-developer.online
Das war Tag 4 vom Spring Boot Aufbau-Kurs!
Du kannst jetzt:
- ✅ Spring Security in ein Projekt integrieren
- ✅ User-Verwaltung in der Datenbank mit JPA
- ✅ Passwörter sicher mit BCrypt verschlüsseln
- ✅ UserDetailsService implementieren
- ✅ SecurityFilterChain konfigurieren
- ✅ Login und Logout Funktionen bauen
- ✅ Die versteckten Security-Features verstehen
- ✅ Session-Management und Remember-Me nutzen
Morgen geht’s um Authorization – wir kontrollieren WER WAS darf! 🔐
Keep coding, keep learning! 💙
Tag 5 erscheint morgen. Bis dahin: Happy Coding!
„Security ist kein Feature – es ist eine Grundvoraussetzung!“ – Elyndra Valen
Tags: #SpringBoot #SpringSecurity #Authentication #UserDetailsService #BCrypt #Security #JavaFleet #Tag4

