@Configuration
public class WebMvcCorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 어떤 경로에 적용할지
.allowedOrigins("https://my-frontend.example.com")
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowedHeaders("*")
.exposedHeaders("X-Total-Count")
.allowCredentials(true) // 쿠키/세션 사용 시
.maxAge(3600);
}
}
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// CORS를 Security에서 처리
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// CSRF는 SPA+API 구조에서 비활성화하는 경우가 흔함(상황에 맞게 결정)
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**", "/actuator/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
// 허용 Origin — 자격 증명(쿠키 등)과 함께 사용 시 반드시 구체 도메인 지정
config.setAllowedOrigins(List.of("https://my-frontend.example.com"));
// 또는 최신 스펙의 패턴 사용: config.addAllowedOriginPattern("https://*.example.com");
config.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setExposedHeaders(List.of("X-Total-Count"));
config.setAllowCredentials(true); // 세션/쿠키 허용 -> true인경우 allowedOrigins에 * 불가.
config.setMaxAge(3600L); // Preflight 캐시
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 특정 경로 매핑
source.registerCorsConfiguration("/api/**", config);
// 모든 경로에 일괄 적용하려면 "/**"
return source;
}
}