fixing(gateway)

This commit is contained in:
2025-08-13 21:46:23 +02:00
parent 562eb07be1
commit b67d75543e
10 changed files with 354 additions and 69 deletions
@@ -1,11 +1,7 @@
package at.mocode.infrastructure.gateway.config
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.cloud.gateway.filter.GlobalFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.http.HttpStatus
import org.springframework.http.server.reactive.ServerHttpRequest
@@ -9,7 +9,7 @@ import java.time.LocalDateTime
/**
* Fallback Controller für Circuit Breaker Szenarien.
* Bietet standardisierte Fehlermeldungen wenn Backend-Services nicht verfügbar sind.
* Bietet standardisierte Fehlermeldungen, wenn Backend-Services nicht verfügbar sind.
*/
@RestController
@RequestMapping("/fallback")
@@ -1,13 +1,10 @@
package at.mocode.infrastructure.gateway.security
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.cloud.gateway.filter.GlobalFilter
import org.springframework.context.annotation.Profile
import org.springframework.core.Ordered
import org.springframework.http.HttpStatus
import org.springframework.http.server.reactive.ServerHttpRequest
import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.stereotype.Component
import org.springframework.util.AntPathMatcher
@@ -42,7 +39,7 @@ class JwtAuthenticationFilter : GlobalFilter, Ordered {
val request = exchange.request
val path = request.path.value()
// Prüfe ob der Pfad öffentlich zugänglich ist
// Prüfe, ob der Pfad öffentlich zugänglich ist
if (isPublicPath(path)) {
return chain.filter(exchange)
}
@@ -56,8 +53,8 @@ class JwtAuthenticationFilter : GlobalFilter, Ordered {
val token = authHeader.substring(7)
// Hier würde normalerweise die JWT-Validierung mit dem auth-client erfolgen
// Für diese Implementation verwenden wir eine vereinfachte Validierung
// Hier würde normalerweise die JWT-Validierung mit dem auth-client erfolgen,
// für diese Implementation verwenden wir eine vereinfachte Validierung
return validateJwtToken(token, exchange, chain)
}
@@ -78,7 +75,7 @@ class JwtAuthenticationFilter : GlobalFilter, Ordered {
return handleUnauthorized(exchange, "Invalid JWT token")
}
// Füge User-Information zu Headers hinzu (simuliert)
// Füge User-Informationen zu Headers hinzu (simuliert)
val userRole = extractUserRole(token)
val userId = extractUserId(token)
@@ -1,32 +1,292 @@
package at.mocode.infrastructure.gateway.security
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.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.reactive.CorsConfigurationSource
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource
import java.time.Duration
/**
* Minimal reactive security configuration for the Gateway.
* Enhanced reactive security configuration for the Gateway.
*
* Rationale:
* ARCHITECTURE OVERVIEW:
* =====================
* This configuration establishes the foundational security layer for the Spring Cloud Gateway.
* It works in conjunction with several other security components:
*
* 1. JwtAuthenticationFilter (GlobalFilter) - Handles JWT token validation and user authentication
* 2. RateLimitingFilter (GlobalFilter) - Provides IP-based rate limiting with user-aware limits
* 3. CorrelationIdFilter (GlobalFilter) - Adds request tracing capabilities
* 4. EnhancedLoggingFilter (GlobalFilter) - Provides structured request/response logging
*
* SECURITY STRATEGY:
* ==================
* The Gateway employs a layered security approach:
* - This SecurityWebFilterChain provides foundational settings (CORS, CSRF, basic headers)
* - JwtAuthenticationFilter handles actual authentication when enabled via property
* - The SecurityWebFilterChain remains permissive (permitAll) to let the JWT filter control access
* - Rate limiting and logging filters provide operational security and monitoring
*
* DESIGN RATIONALE:
* =================
* - During tests, Spring Security is on the classpath (testImplementation), which enables
* security auto-configuration and can lock down all endpoints unless a SecurityWebFilterChain is provided.
* - The Gateway enforces auth using a GlobalFilter (JwtAuthenticationFilter) when enabled via property,
* so the SecurityWebFilterChain should stay permissive and let the filter do the auth work.
* security autoconfiguration and can lock down all endpoints unless a SecurityWebFilterChain is provided
* - The Gateway enforces authentication using JwtAuthenticationFilter when enabled via property,
* so the SecurityWebFilterChain should stay permissive and focus on foundational concerns
* - Explicit CORS configuration ensures proper handling of cross-origin requests from web clients
* - Configurable properties allow environment-specific security settings without code changes
* - CSRF protection is disabled as it's not needed for stateless JWT-based authentication
*
* CORS INTEGRATION:
* =================
* The CORS configuration works with the existing filter chain:
* - Allows requests from configured origins (dev/prod environments)
* - Exposes custom headers from Gateway filters (correlation IDs, rate limits)
* - Supports credentials for JWT authentication
* - Caches preflight responses for performance
*
* TESTING CONSIDERATIONS:
* =======================
* - Configuration is designed to work seamlessly with existing security tests
* - Test profile can override CORS settings if needed
* - Permissive authorization ensures tests can focus on filter-level security
*/
@Configuration
class SecurityConfig {
@EnableConfigurationProperties(GatewaySecurityProperties::class)
class SecurityConfig(
private val securityProperties: GatewaySecurityProperties
) {
/**
* Main Spring Security filter chain configuration.
*
* This method configures the reactive security filter chain with:
* - CSRF disabled for stateless API operation
* - Explicit CORS configuration for cross-origin support
* - Permissive authorization (authentication handled by JWT filter)
*
* The configuration maintains compatibility with the existing filter architecture
* while providing enhanced CORS control and configurability.
*/
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.csrf { it.disable() }
.cors { }
fun springSecurityFilterChain(): SecurityWebFilterChain {
return ServerHttpSecurity.http()
.csrf { csrf ->
// Disable CSRF for stateless API gateway
// CSRF protection is not required for JWT-based stateless authentication
// The Gateway operates as a stateless proxy with no session state
csrf.disable()
}
.cors { cors ->
// Use explicit CORS configuration instead of default
// This provides better control over cross-origin access policies
cors.configurationSource(corsConfigurationSource())
}
.httpBasic { basic ->
// Disable HTTP Basic auth for stateless API
basic.disable()
}
.formLogin { form ->
// Disable form login for API gateway
form.disable()
}
.authorizeExchange { exchanges ->
exchanges
.anyExchange().permitAll()
// Allow all requests through Spring Security
// Authentication and authorization are handled by JwtAuthenticationFilter
// This approach maintains the existing security architecture while
// allowing the JWT filter to make granular access control decisions
exchanges.anyExchange().permitAll()
}
.build()
}
/**
* Explicit CORS configuration source.
*
* This bean provides detailed control over cross-origin resource sharing settings,
* replacing the default empty CORS configuration with explicit, configurable settings.
*
* Key features:
* - Environment-specific allowed origins
* - Comprehensive HTTP method support
* - JWT-aware header configuration
* - Integration with Gateway filter headers
* - Performance-optimized preflight caching
*
* The configuration is designed to work with typical web application architectures
* where a JavaScript frontend makes API calls to the Gateway.
*/
@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration().apply {
// Allowed origins - configurable per environment
// Development: localhost URLs for local testing
// Production: domain-specific URLs for deployed applications
allowedOrigins = securityProperties.cors.allowedOrigins.toList()
// Allowed HTTP methods - comprehensive REST API support
// Includes all standard methods plus OPTIONS for preflight requests
allowedMethods = securityProperties.cors.allowedMethods.toList()
// Allowed request headers - includes JWT and custom headers
// Authorization: for JWT Bearer tokens
// X-Correlation-ID: for request tracing
// Standard headers: Content-Type, Accept, etc.
allowedHeaders = securityProperties.cors.allowedHeaders.toList()
// Exposed response headers - allows client access to custom headers
// Includes headers added by Gateway filters:
// - X-Correlation-ID from CorrelationIdFilter
// - X-RateLimit-* from RateLimitingFilter
exposedHeaders = securityProperties.cors.exposedHeaders.toList()
// Allow credentials - required for JWT authentication
// Enables cookies and authorization headers in cross-origin requests
allowCredentials = securityProperties.cors.allowCredentials
// Preflight cache duration - performance optimization
// Reduces the number of OPTIONS requests for repeated API calls
maxAge = securityProperties.cors.maxAge.seconds
}
return UrlBasedCorsConfigurationSource().apply {
// Apply CORS configuration to all Gateway routes
registerCorsConfiguration("/**", configuration)
}
}
}
/**
* Configuration properties for Gateway security settings.
*
* Enables environment-specific security configuration via application.yml/properties.
* This approach allows different security settings across development, testing, and
* production environments without requiring code changes.
*
* Example application.yml configuration:
* ```yaml
* gateway:
* security:
* cors:
* allowed-origins:
* - http://localhost:3000
* - https://app.meldestelle.at
* allowed-methods:
* - GET
* - POST
* - PUT
* - DELETE
* allow-credentials: true
* max-age: PT2H
* ```
*/
@ConfigurationProperties(prefix = "gateway.security")
data class GatewaySecurityProperties(
val cors: CorsProperties = CorsProperties()
)
/**
* CORS-specific configuration properties with sensible defaults.
*
* Default values are chosen to work with typical development and production setups:
* - Common development URLs (localhost with standard ports)
* - Production domain pattern matching
* - Full REST API method support
* - JWT and Gateway filter header support
* - Reasonable preflight cache duration
*/
data class CorsProperties(
/**
* Allowed origins for CORS requests.
*
* Defaults support common development and production scenarios:
* - localhost:3000 - typical React development server
* - localhost:8080 - common alternative development port
* - localhost:4200 - typical Angular development server
* - Specific meldestelle.at subdomains for production
*
* Can be overridden per environment as needed.
*/
val allowedOrigins: Set<String> = setOf(
"http://localhost:3000",
"http://localhost:8080",
"http://localhost:4200",
"https://app.meldestelle.at",
"https://frontend.meldestelle.at",
"https://www.meldestelle.at"
),
/**
* Allowed HTTP methods for CORS requests.
*
* Includes all standard REST API methods plus OPTIONS for preflight
* and HEAD for metadata requests.
*/
val allowedMethods: Set<String> = setOf(
"GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH"
),
/**
* Allowed request headers for CORS requests.
*
* Includes:
* - Standard headers: Content-Type, Accept, etc.
* - JWT authentication: Authorization
* - Gateway tracing: X-Correlation-ID
* - Cache control: Cache-Control, Pragma
*/
val allowedHeaders: Set<String> = setOf(
"Authorization",
"Content-Type",
"X-Requested-With",
"X-Correlation-ID",
"Accept",
"Origin",
"Cache-Control",
"Pragma"
),
/**
* Exposed response headers for CORS requests.
*
* Headers that client JavaScript can access in responses.
* Includes custom headers added by Gateway filters:
* - X-Correlation-ID: request tracing (CorrelationIdFilter)
* - X-RateLimit-*: rate limiting info (RateLimitingFilter)
* - Standard headers: Content-Length, Date
*/
val exposedHeaders: Set<String> = setOf(
"X-Correlation-ID",
"X-RateLimit-Limit",
"X-RateLimit-Remaining",
"X-RateLimit-Enabled",
"Content-Length",
"Date"
),
/**
* Allow credentials in CORS requests.
*
* Set to true to support:
* - JWT Bearer tokens in Authorization headers
* - Cookies (if used)
* - Client certificates (if used)
*/
val allowCredentials: Boolean = true,
/**
* Maximum age for preflight request caching.
*
* Duration that browsers can cache preflight responses, reducing
* the number of OPTIONS requests for repeated API calls.
* Default: 1 hour (reasonable balance of performance vs. flexibility)
*/
val maxAge: Duration = Duration.ofHours(1)
)