GatewayConfiguration.java

package org.petify.gateway.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
// import org.springframework.security.config.Customizer;
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.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;

/**
 * Klasa konfiguracyjna dla Gateway - zawiera zarówno konfigurację security jak i filtry.
 */
@Configuration
@EnableWebFluxSecurity
public class GatewayConfiguration {

    private static final Logger log = LoggerFactory.getLogger(GatewayConfiguration.class);

    /**
     * Konfiguracja bezpieczeństwa
     */
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchanges -> exchanges
                        .anyExchange().permitAll()
                )
                .build();
    }

    /**
     * Konfiguracja CORS
     */
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowedOriginPatterns(Arrays.asList(
                "http://localhost:*",
                "http://127.0.0.1:*"
        ));
        corsConfig.setMaxAge(3600L);
        corsConfig.addAllowedMethod(CorsConfiguration.ALL);
        corsConfig.addAllowedHeader(CorsConfiguration.ALL);
        corsConfig.setExposedHeaders(Arrays.asList("Authorization"));

        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);
        return new CorsWebFilter(source);
    }

    /**
     * Filtr do logowania żądań i odpowiedzi
     */
    @Bean
    public GlobalFilter loggingFilter() {
        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();

                // Logowanie szczegółów żądania
                log.info("Gateway Request: {} {}", request.getMethod(), request.getPath());

                // Logowanie nagłówków (z ukryciem wrażliwych danych)
                request.getHeaders().forEach((name, values) -> {
                    values.forEach(value -> {
                        if (name.equalsIgnoreCase("authorization")) {
                            if (value.startsWith("Bearer ") && value.length() > 15) {
                                log.info("Header: {}: Bearer {}...", name, value.substring(7, 15));
                            } else {
                                log.info("Header: {}: [masked]", name);
                            }
                        } else {
                            log.info("Header: {}: {}", name, value);
                        }
                    });
                });

                // Logowanie odpowiedzi
                return chain.filter(exchange)
                        .then(Mono.fromRunnable(() -> {
                            ServerHttpResponse response = exchange.getResponse();
                            log.info("Gateway Response: {} for {} {}",
                                    response.getStatusCode(),
                                    request.getMethod(),
                                    request.getPath());
                        }));
            }
        };
    }

    /**
     * Filtr do przekazywania nagłówka Authorization
     */
    @Bean
    public GlobalFilter authHeaderFilter() {
        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();

                if (request.getHeaders().containsKey("Authorization")) {
                    String authHeader = request.getHeaders().getFirst("Authorization");

                    // Utwórz nowe żądanie z jawnie przekazanym nagłówkiem Authorization
                    ServerHttpRequest modifiedRequest = request.mutate()
                            .header("Authorization", authHeader)
                            .build();

                    return chain.filter(exchange.mutate().request(modifiedRequest).build());
                }

                return chain.filter(exchange);
            }
        };
    }
}