package com.paymentlink.config; 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.web.SecurityFilterChain; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.web.csrf.CsrfTokenRequestHandler; import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler; import java.util.Arrays; import java.util.function.Supplier; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // Disable authentication - we want permitAll .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() ) // Disable CSRF for easier testing (re-enable in production) .csrf(csrf -> csrf.disable()) // CORS Configuration .cors(cors -> cors.configurationSource(corsConfigurationSource())); return http.build(); } /** * Custom CSRF token request handler for SPA */ static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler { private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler(); @Override public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) { delegate.handle(request, response, csrfToken); } @Override public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { String headerValue = request.getHeader(csrfToken.getHeaderName()); return (headerValue != null) ? headerValue : delegate.resolveCsrfTokenValue(request, csrfToken); } } /** * CORS Configuration */ @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("*")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("*")); configuration.setAllowCredentials(false); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } /** * Custom HTTP Firewall to allow WebDAV methods (PROPFIND, etc.) * This prevents RequestRejectedException logs from IDEs like WebStorm */ @Bean public HttpFirewall allowWebDavHttpFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); // Allow standard HTTP methods plus WebDAV methods firewall.setAllowedHttpMethods(Arrays.asList( "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", // WebDAV methods used by IDEs "PROPFIND", "PROPPATCH", "REPORT", "LOCK", "UNLOCK", "COPY", "MOVE", "MKCOL" )); return firewall; } }