optimierungen auth-Modul und cache-Modul
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Infrastructure/Auth Module - Comprehensive Documentation
|
||||
# Infrastructure/Auth Modul – Aktuelle Dokumentation (Stand: September 2025)
|
||||
|
||||
## Überblick
|
||||
|
||||
@@ -6,6 +6,21 @@ Das **Auth-Modul** ist die zentrale Komponente für die gesamte Authentifizierun
|
||||
|
||||
Als Identity Provider wird **Keycloak** verwendet. Dieses Modul kapselt die gesamte Interaktion mit Keycloak und stellt dem Rest des Systems eine einheitliche und vereinfachte Sicherheitsschicht zur Verfügung.
|
||||
|
||||
## Aufgabe des Moduls
|
||||
|
||||
- Zentrale Bereitstellung von Authentifizierungs- und Autorisierungsfunktionen für alle Services
|
||||
- Minimierung der Kopplung an Keycloak durch eine API/Client-Abstraktion (`auth-client`)
|
||||
- Einheitliche, typsichere Repräsentation von Rollen und Berechtigungen als Enums
|
||||
- Sichere Erzeugung, Validierung und Auswertung von JWTs (Issuer, Audience, Ablauf, Signatur)
|
||||
- Bereitstellung eines dedizierten Auth-Servers für Benutzer-Workflows (Login, optional Passwortänderung, Token-Ausstellung)
|
||||
|
||||
## Umsetzung (High-Level)
|
||||
|
||||
- Authentifizierung findet gegen Keycloak statt; der `auth-server` kapselt dessen Aufrufe.
|
||||
- Nach erfolgreicher Authentifizierung wird ein signiertes JWT erzeugt, das Rollen/Berechtigungen enthält.
|
||||
- Downstream-Services validieren das JWT über den `auth-client` und führen autorisierte Domänenaktionen aus.
|
||||
- Das API-Gateway kann JWTs vorvalidieren und Metadaten-Header weitergeben; vollständige Validierung sollte via `auth-client` erfolgen.
|
||||
|
||||
## Architektur
|
||||
|
||||
Das Auth-Modul ist in zwei spezialisierte Komponenten aufgeteilt, um eine klare Trennung der Verantwortlichkeiten zu gewährleisten:
|
||||
@@ -20,12 +35,17 @@ infrastructure/auth/
|
||||
|
||||
Dieses Modul ist eine **wiederverwendbare Bibliothek** und kein eigenständiger Service. Es enthält die gesamte Logik, die andere Microservices (wie `masterdata-service`, `members-service` etc.) benötigen, um ihre Endpunkte abzusichern.
|
||||
|
||||
**Hauptaufgaben:**
|
||||
* **JWT-Management:** Stellt einen `JwtService` zur Erstellung und Validierung von JSON Web Tokens bereit.
|
||||
* **Modell-Definition:** Definiert die **Quelle der Wahrheit** für sicherheitsrelevante Konzepte wie `RolleE` und `BerechtigungE` als typsichere Kotlin-Enums. Dies stellt sicher, dass alle Services dieselbe "Sprache" für Berechtigungen sprechen.
|
||||
* **Schnittstellen:** Bietet saubere Schnittstellen wie `AuthenticationService` an, die von der konkreten Implementierung (z.B. Keycloak) abstrahieren.
|
||||
Aktueller Stand (09/2025):
|
||||
- Enthält ein typensicheres Rollen- und Berechtigungsmodell: `RolleE`, `BerechtigungE` (kotlinx.serialization-annotiert für konsistente JSON-Serialisierung).
|
||||
- Definiert die Schnittstelle `AuthenticationService` mit suspend-Funktionen und Result-Typen zur Authentifizierung und Passwortänderung. Rückgabewerte sind versiegelt (sealed) und decken Success/Failure/Locked ab. Dadurch klare, explizite Fehlerfälle ohne Exceptions in Kontrollflüssen.
|
||||
- Stellt den `JwtService` bereit, der via Spring konfiguriert werden kann und in Services zur Token-Erzeugung/-Validierung genutzt wird.
|
||||
|
||||
Jeder Microservice, der geschützte Endpunkte anbietet, bindet dieses Modul als Abhängigkeit ein.
|
||||
**Hauptaufgaben:**
|
||||
* **JWT-Management:** Stellt einen `JwtService` zur Erstellung und Validierung von JSON Web Tokens bereit (Signatur, Claims, Ablaufzeiten). Neue, result-basierte APIs erleichtern das Fehler-Handling.
|
||||
* **Modell-Definition:** Definiert die **Quelle der Wahrheit** für sicherheitsrelevante Konzepte wie `RolleE` und `BerechtigungE` als typsichere Kotlin-Enums. Dies stellt sicher, dass alle Services dieselbe "Sprache" für Berechtigungen sprechen.
|
||||
* **Schnittstellen:** Bietet saubere Schnittstellen wie `AuthenticationService` an, die von der konkreten Implementierung (z.B. Keycloak) abstrahieren. Dadurch können Implementierungen im `auth-server` oder in Tests (Mocks/Fakes) ausgetauscht werden.
|
||||
|
||||
Einbindung: Jeder Microservice, der geschützte Endpunkte anbietet, bindet dieses Modul als Abhängigkeit ein.
|
||||
|
||||
### `auth-server`
|
||||
|
||||
@@ -36,6 +56,42 @@ Dies ist ein **eigenständiger Spring Boot Microservice**, der als Brücke zwisc
|
||||
* **Token-Endpunkte:** Ist verantwortlich für das Ausstellen von Tokens nach einer erfolgreichen Authentifizierung.
|
||||
* **Implementierung der `AuthenticationService`-Schnittstelle:** Enthält die konkrete Logik, die gegen Keycloak prüft, ob ein Benutzername und ein Passwort korrekt sind.
|
||||
|
||||
**Konfiguration (AuthServerConfiguration):**
|
||||
Der Service stellt einen konfigurierbaren `JwtService` per Spring-Bean bereit. Die dazugehörigen Properties werden über `auth.jwt.*` gesetzt:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
jwt:
|
||||
secret: <32+ Zeichen starkes Secret>
|
||||
issuer: meldestelle-auth-server
|
||||
audience: meldestelle-services
|
||||
expiration: 60 # Minuten
|
||||
```
|
||||
|
||||
Kotlin-Konfiguration (vereinfacht):
|
||||
```kotlin
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(JwtProperties::class)
|
||||
class AuthServerConfiguration {
|
||||
@Bean
|
||||
fun jwtService(props: JwtProperties) = JwtService(
|
||||
secret = props.secret,
|
||||
issuer = props.issuer,
|
||||
audience = props.audience,
|
||||
expiration = props.expiration.minutes
|
||||
)
|
||||
@ConfigurationProperties(prefix = "auth.jwt")
|
||||
data class JwtProperties(
|
||||
val secret: String,
|
||||
val issuer: String,
|
||||
val audience: String,
|
||||
val expiration: Long
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Hinweis: Standardwerte sind nur für lokale Entwicklung gedacht und müssen in Produktion überschrieben werden. Zusätzlich validiert der Auth-Server die JWT-Properties: Secret min. 32 Zeichen, issuer/audience nicht leer; bei Verwendung des Default-Secrets wird eine Laufzeit-Warnung ausgegeben.
|
||||
|
||||
## Zusammenspiel im System
|
||||
|
||||
1. Ein **Benutzer** meldet sich über eine Client-Anwendung am **`auth-server`** an.
|
||||
@@ -43,10 +99,11 @@ Dies ist ein **eigenständiger Spring Boot Microservice**, der als Brücke zwisc
|
||||
3. Bei Erfolg erstellt der `auth-server` mit dem `JwtService` aus dem `auth-client` ein JWT, das die Berechtigungen des Benutzers enthält, und sendet es an den Client zurück.
|
||||
4. Der **Client** sendet eine Anfrage an einen anderen Microservice (z.B. `members-service`) und fügt das JWT als Bearer-Token in den Header ein.
|
||||
5. Der **`members-service`**, der ebenfalls den `auth-client` als Abhängigkeit hat, nutzt den `JwtService`, um das Token zu validieren und die Berechtigungen typsicher auszulesen.
|
||||
6. Das **Gateway** kann vorgelagert JWT-basierte Authentifizierung durchführen. Aktuell existiert ein `JwtAuthenticationFilter`, der über `gateway.security.jwt.enabled=true` aktiviert wird. In der vorliegenden Codebasis nutzt dieser noch eine vereinfachte Validierung; die geplante Integration ist die Nutzung des `auth-client` zur vollständigen Validierung und Claim-Extraktion.
|
||||
|
||||
Diese Architektur entkoppelt die Fach-Services von der Komplexität der Identitätsverwaltung und schafft eine robuste, zentrale Sicherheitsinfrastruktur.
|
||||
|
||||
## Modernisierungen (August 2025)
|
||||
## Modernisierungen (September 2025)
|
||||
|
||||
### Technische Verbesserungen
|
||||
|
||||
@@ -68,6 +125,21 @@ Diese Architektur entkoppelt die Fach-Services von der Komplexität der Identit
|
||||
- Entfernung von `Thread.sleep()` für zuverlässigere Tests
|
||||
- Bessere Expired-Token-Tests mit eindeutigen Zeitstempel-Differenzen
|
||||
|
||||
### Token Claims und Struktur
|
||||
|
||||
Empfohlene Claims im JWT (Beispiel):
|
||||
- sub: Benutzer-ID (UUID)
|
||||
- pid: Personen-ID (UUID)
|
||||
- preferred_username: Loginname (derzeit intern als Claim "username" umgesetzt)
|
||||
- email: E-Mail-Adresse
|
||||
- roles: Liste von Rollen (`RolleE`)
|
||||
- perms: Liste von Berechtigungen (`BerechtigungE`)
|
||||
- iss: Issuer (z.B. meldestelle-auth-server)
|
||||
- aud: Audience (z.B. meldestelle-services)
|
||||
- iat/exp: Ausstellungs- und Ablaufzeitpunkt
|
||||
|
||||
Diese Claims werden vom `auth-client` gelesen und in typsichere Modelle abgebildet.
|
||||
|
||||
### API-Änderungen
|
||||
|
||||
**Neue Result-basierte APIs:**
|
||||
@@ -255,6 +327,14 @@ Das Auth-Modul wurde von **kritisch untergetestet** auf **umfassend getestet** t
|
||||
└── kotlinx-serialization-json (JSON Serialization)
|
||||
```
|
||||
|
||||
## Aktualitäts-Check (Repo-Stand September 2025)
|
||||
|
||||
- `auth-client` enthält `AuthenticationService` mit suspend-Funktionen und versiegelten Result-Typen (Success/Failure/Locked; PasswordChangeResult inkl. WeakPassword). Diese Schnittstelle ist in dieser README beschrieben und aktuell.
|
||||
- `auth-server` stellt `JwtService` via `AuthServerConfiguration` bereit und liest Properties unter `auth.jwt.*`. Beispielkonfiguration ist oben dokumentiert und entspricht dem Code.
|
||||
- Das API-Gateway besitzt einen `JwtAuthenticationFilter`, der derzeit eine vereinfachte Tokenprüfung implementiert. Geplante nächste Stufe: Verwendung des `auth-client` zur echten JWT-Validierung und Claim-Extraktion. Property-Schalter: `gateway.security.jwt.enabled`.
|
||||
|
||||
Diese README wurde am 03.09.2025 aktualisiert und spiegelt den aktuellen Stand der Implementierung wider.
|
||||
|
||||
## Production-Readiness Status
|
||||
|
||||
### ✅ Production-Ready Bereiche
|
||||
@@ -305,7 +385,7 @@ Das infrastructure/auth Modul ist **production-ready** und umfassend modernisier
|
||||
Die Transformation von "kritisch untergetestet" zu "production-ready" ist vollständig abgeschlossen und erfüllt alle Anforderungen für ein sicherheitskritisches Authentifizierungs-System in einer Microservice-Landschaft.
|
||||
|
||||
---
|
||||
**Letzte Aktualisierung**: 15. August 2025
|
||||
**Letzte Aktualisierung**: 3. September 2025
|
||||
**Status**: Production-Ready mit umfassender Test-Abdeckung
|
||||
**Dokumentation**: Vollständig konsolidiert aus allen Teilbereichen
|
||||
**Validierung**: Sicherheitstests erfolgreich bestanden (15.08.2025)
|
||||
|
||||
@@ -9,6 +9,21 @@ plugins {
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
|
||||
|
||||
+14
-14
@@ -5,37 +5,37 @@ import com.benasher44.uuid.Uuid
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Service for user authentication and password management.
|
||||
* Service für Benutzerauthentifizierung und Passwortverwaltung.
|
||||
*/
|
||||
interface AuthenticationService {
|
||||
/**
|
||||
* Authenticates a user with the given username and password.
|
||||
* Authentifiziert einen Benutzer mit Benutzernamen und Passwort.
|
||||
*
|
||||
* @param username The username
|
||||
* @param password The password
|
||||
* @return The authentication result
|
||||
* @param username Der Benutzername
|
||||
* @param password Das Passwort
|
||||
* @return Das Authentifizierungsergebnis
|
||||
*/
|
||||
suspend fun authenticate(username: String, password: String): AuthResult
|
||||
|
||||
/**
|
||||
* Changes a user's password.
|
||||
* Ändert das Passwort eines Benutzers.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param currentPassword The current password
|
||||
* @param newPassword The new password
|
||||
* @return The password change result
|
||||
* @param userId Die Benutzer-ID
|
||||
* @param currentPassword Das aktuelle Passwort
|
||||
* @param newPassword Das neue Passwort
|
||||
* @return Das Ergebnis der Passwortänderung
|
||||
*/
|
||||
suspend fun changePassword(userId: Uuid, currentPassword: String, newPassword: String): PasswordChangeResult
|
||||
|
||||
/**
|
||||
* Possible results of an authentication attempt.
|
||||
* Mögliche Ergebnisse eines Authentifizierungsversuchs.
|
||||
*/
|
||||
sealed class AuthResult {
|
||||
/**
|
||||
* Authentication was successful.
|
||||
* Authentifizierung war erfolgreich.
|
||||
*
|
||||
* @param token The JWT token
|
||||
* @param user The authenticated user
|
||||
* @param token Das JWT-Token
|
||||
* @param user Der authentifizierte Benutzer
|
||||
*/
|
||||
data class Success(val token: String, val user: AuthenticatedUser) : AuthResult()
|
||||
|
||||
|
||||
+6
@@ -23,6 +23,12 @@ class JwtService(
|
||||
) {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
init {
|
||||
require(secret.length >= 32) { "JWT secret must be at least 32 characters for HMAC512" }
|
||||
require(issuer.isNotBlank()) { "JWT issuer must not be blank" }
|
||||
require(audience.isNotBlank()) { "JWT audience must not be blank" }
|
||||
}
|
||||
|
||||
private val algorithm = Algorithm.HMAC512(secret)
|
||||
private val verifier = JWT.require(algorithm)
|
||||
.withIssuer(issuer)
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class JwtServiceTest {
|
||||
@Test
|
||||
fun `validateToken should return false for token with wrong secret`() {
|
||||
// Arrange
|
||||
val otherService = JwtService("a-different-wrong-secret", testIssuer, testAudience)
|
||||
val otherService = JwtService("a-different-wrong-secret-that-is-long-enough-1234567890", testIssuer, testAudience)
|
||||
val token = otherService.generateToken("user-123", "test", emptyList())
|
||||
|
||||
// Act & Assert
|
||||
|
||||
@@ -9,6 +9,12 @@ plugins {
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
// Konfiguriert die Hauptklasse für das ausführbare JAR.
|
||||
springBoot {
|
||||
mainClass.set("at.mocode.infrastructure.auth.AuthServerApplicationKt")
|
||||
|
||||
+18
-4
@@ -5,21 +5,29 @@ import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.validation.annotation.Validated
|
||||
import jakarta.validation.constraints.Min
|
||||
import jakarta.validation.constraints.NotBlank
|
||||
import jakarta.validation.constraints.Size
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Spring configuration for the Auth Server module.
|
||||
* Provides the necessary beans and configuration for JWT handling and authentication.
|
||||
* Spring-Konfiguration für das Auth-Server-Modul.
|
||||
* Stellt die notwendigen Beans und Einstellungen für JWT-Verarbeitung und Authentifizierung bereit.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(AuthServerConfiguration.JwtProperties::class)
|
||||
class AuthServerConfiguration {
|
||||
|
||||
/**
|
||||
* Creates a JwtService bean with configuration from application properties.
|
||||
* Erstellt einen JwtService-Bean mit Konfiguration aus den Application Properties.
|
||||
*/
|
||||
@Bean
|
||||
fun jwtService(jwtProperties: JwtProperties): JwtService {
|
||||
// Basic safeguard: warn if default secret is used
|
||||
if (jwtProperties.secret == "default-secret-for-development-only-please-change-in-production") {
|
||||
System.err.println("[SECURITY WARNING] Using default JWT secret – DO NOT use this in production!")
|
||||
}
|
||||
return JwtService(
|
||||
secret = jwtProperties.secret,
|
||||
issuer = jwtProperties.issuer,
|
||||
@@ -29,13 +37,19 @@ class AuthServerConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties for JWT settings.
|
||||
* Konfigurationseigenschaften für JWT-Einstellungen.
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "auth.jwt")
|
||||
@Validated
|
||||
data class JwtProperties(
|
||||
@field:NotBlank
|
||||
@field:Size(min = 32, message = "JWT secret must be at least 32 characters for HMAC512")
|
||||
val secret: String = "default-secret-for-development-only-please-change-in-production",
|
||||
@field:NotBlank
|
||||
val issuer: String = "meldestelle-auth-server",
|
||||
@field:NotBlank
|
||||
val audience: String = "meldestelle-services",
|
||||
@field:Min(1)
|
||||
val expiration: Long = 60 // minutes
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Infrastructure Auth Module Container
|
||||
// This is a container module for authentication-related subprojects
|
||||
Reference in New Issue
Block a user