/** * Security Configuration * * Spring Security configuration for JWT-based authentication. * * @author David Valera Melendez * @since February 2025 */ package com.company.auth.config; import com.company.auth.security.JwtAuthenticationEntryPoint; import com.company.auth.security.JwtAuthenticationFilter; import com.company.auth.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 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.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; import java.util.List; /** * Security Configuration * * Configures Spring Security for: * - JWT-based authentication * - Stateless session management * - CORS configuration * - Security filters and providers */ @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; @Autowired private UserService userService; @Autowired private PasswordEncoder passwordEncoder; /** * Authentication provider bean * * @return DAO authentication provider */ @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userService); authProvider.setPasswordEncoder(passwordEncoder); return authProvider; } /** * Authentication manager bean * * @param config Authentication configuration * @return Authentication manager * @throws Exception if configuration fails */ @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } /** * CORS configuration source * * @return CORS configuration source */ @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // Allow specific origins in production, * for development configuration.setAllowedOriginPatterns(Arrays.asList( "http://localhost:3000", // React development "http://localhost:4200", // Angular development "http://localhost:8080", // Local backend "https://*.company.com", // Production domains "https://company.com" )); configuration.setAllowedMethods(Arrays.asList( "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS" )); configuration.setAllowedHeaders(Arrays.asList( "Authorization", "Content-Type", "X-Requested-With", "Accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", // Custom headers for device fingerprinting "User-Agent", "Accept-Language", "Timezone", "Screen-Resolution", "Color-Depth", "Hardware-Concurrency", "Device-Memory", "Platform", "Cookie-Enabled", "DNT", "Webdriver", "Device-Fingerprint" )); configuration.setExposedHeaders(Arrays.asList( "Authorization", "Content-Disposition" )); configuration.setAllowCredentials(true); configuration.setMaxAge(3600L); // 1 hour preflight cache UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } /** * Security filter chain configuration * * @param http HTTP security configuration * @return Security filter chain * @throws Exception if configuration fails */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // Disable CSRF for stateless REST APIs .csrf(AbstractHttpConfigurer::disable) // Configure CORS .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Configure authorization rules .authorizeHttpRequests(authz -> authz // Public endpoints .requestMatchers( "/api/auth/register", "/api/auth/login", "/api/auth/verify-device", "/api/auth/verify-2fa", "/api/auth/refresh", "/api/auth/resend-device-verification", "/api/auth/resend-2fa" ).permitAll() // Health check and monitoring endpoints .requestMatchers( "/actuator/health", "/actuator/health/**", "/actuator/info" ).permitAll() // API documentation endpoints .requestMatchers( "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/swagger-resources/**", "/webjars/**" ).permitAll() // Static resources .requestMatchers( "/favicon.ico", "/error" ).permitAll() // All other endpoints require authentication .anyRequest().authenticated() ) // Configure exception handling .exceptionHandling(ex -> ex .authenticationEntryPoint(jwtAuthenticationEntryPoint) ) // Configure session management (stateless) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) // Configure authentication provider .authenticationProvider(authenticationProvider()) // Add JWT authentication filter .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }