chore(ping-service, security): integrate centralized security module and enhance Ping-Service
- Replaced local `SecurityConfig` in `ping-service` with the shared `infrastructure:security` module. - Added `GlobalSecurityConfig` to standardize OAuth2, JWT validation, and CORS for all services. - Introduced new endpoints (`/ping/public`, `/ping/secure`) with role-based access control. - Updated database schema with Flyway migration (`V1__init_ping.sql`) and refactored persistence layer to align with the standardized approach (`createdAt` field). - Enhanced application configuration (`application.yaml`) to use shared security and Flyway settings.
This commit is contained in:
@@ -25,7 +25,12 @@ spring:
|
||||
response-timeout: 5s
|
||||
routes:
|
||||
- id: ping-service
|
||||
uri: http://ping-service:8080
|
||||
# Nutze lb:// wenn Service Discovery aktiv ist, sonst http://hostname:port
|
||||
# Da wir Consul nutzen, ist lb://ping-service besser, aber für Tracer Bullet
|
||||
# und direkte Docker-Kommunikation ist http://ping-service:8082 sicherer,
|
||||
# falls Consul noch nicht 100% stabil ist.
|
||||
# Wir nutzen hier den Docker Alias und den konfigurierten Port.
|
||||
uri: http://ping-service:8082
|
||||
predicates:
|
||||
- Path=/api/ping/**
|
||||
filters:
|
||||
@@ -42,13 +47,12 @@ management:
|
||||
include: health,info,prometheus
|
||||
tracing:
|
||||
sampling:
|
||||
probability: 1.0 # 100% der Requests tracen (für Dev/Test sinnvoll, in Prod reduzieren)
|
||||
probability: 1.0
|
||||
propagation:
|
||||
type: w3c # Standard W3C Trace Context (kompatibel mit OpenTelemetry)
|
||||
type: w3c
|
||||
|
||||
# Gateway-spezifische Einstellungen
|
||||
gateway:
|
||||
ratelimit:
|
||||
enabled: false # Start: ausgeschaltet; zum Aktivieren default-filters plus RequestRateLimiter in YAML hinzufügen
|
||||
enabled: false
|
||||
replenish-rate: 10
|
||||
burst-capacity: 20
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package at.mocode.infrastructure.security
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.http.SessionCreationPolicy
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.web.cors.CorsConfiguration
|
||||
import org.springframework.web.cors.CorsConfigurationSource
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(prePostEnabled = true) // Erlaubt @PreAuthorize in Services/Controllern
|
||||
class GlobalSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http
|
||||
.csrf { it.disable() } // CSRF nicht nötig für Stateless REST APIs
|
||||
.cors { it.configurationSource(corsConfigurationSource()) }
|
||||
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
|
||||
.authorizeHttpRequests { auth ->
|
||||
// Explizite Freigaben (Health, Info, Public Endpoints)
|
||||
auth.requestMatchers("/actuator/**").permitAll()
|
||||
auth.requestMatchers("/ping/public").permitAll()
|
||||
auth.requestMatchers("/error").permitAll()
|
||||
|
||||
// Alles andere muss authentifiziert sein
|
||||
auth.anyRequest().authenticated()
|
||||
}
|
||||
.oauth2ResourceServer { oauth2 ->
|
||||
oauth2.jwt { jwt ->
|
||||
jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
|
||||
val converter = JwtAuthenticationConverter()
|
||||
converter.setJwtGrantedAuthoritiesConverter(KeycloakRoleConverter())
|
||||
return converter
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
// Erlaube Frontend (localhost, docker host)
|
||||
configuration.allowedOriginPatterns = listOf("*") // Für Dev; in Prod einschränken!
|
||||
configuration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
configuration.allowedHeaders = listOf("*")
|
||||
configuration.allowCredentials = true
|
||||
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
source.registerCorsConfiguration("/**", configuration)
|
||||
return source
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package at.mocode.infrastructure.security
|
||||
|
||||
import org.springframework.core.convert.converter.Converter
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
|
||||
/**
|
||||
* Konvertiert Keycloak-Rollen aus dem JWT (Realm Access & Resource Access)
|
||||
* in Spring Security GrantedAuthorities.
|
||||
*
|
||||
* Erwartetes Format im Token:
|
||||
* "realm_access": { "roles": ["admin", "user"] }
|
||||
* "resource_access": { "my-client": { "roles": ["client-role"] } }
|
||||
*/
|
||||
class KeycloakRoleConverter : Converter<Jwt, Collection<GrantedAuthority>> {
|
||||
|
||||
override fun convert(jwt: Jwt): Collection<GrantedAuthority> {
|
||||
val roles = mutableSetOf<String>()
|
||||
|
||||
// 1. Realm Roles extrahieren
|
||||
val realmAccess = jwt.claims["realm_access"] as? Map<*, *>
|
||||
if (realmAccess != null) {
|
||||
(realmAccess["roles"] as? List<*>)?.forEach { role ->
|
||||
if (role is String) {
|
||||
roles.add(role)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Resource (Client) Roles extrahieren
|
||||
// Optional: Falls wir Client-spezifische Rollen brauchen.
|
||||
// Hier mappen wir vorerst nur Realm-Rollen global.
|
||||
|
||||
// 3. Mapping zu GrantedAuthority (Prefix "ROLE_" ist Standard in Spring Security)
|
||||
return roles.map { role ->
|
||||
SimpleGrantedAuthority("ROLE_${role.uppercase()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user