Files
Java/src/main/java/com/company/auth/config/SecurityConfig.java
David Melendez 1c026c7be8 init commit
2026-01-14 22:41:30 +01:00

222 lines
7.7 KiB
Java

/**
* Security Configuration
*
* Spring Security configuration for JWT-based authentication.
*
* @author David Valera Melendez <david@valera-melendez.de>
* @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();
}
}