package com.kamco.cd.kamcoback.config; import com.kamco.cd.kamcoback.auth.CustomAuthenticationProvider; import com.kamco.cd.kamcoback.auth.JwtAuthenticationFilter; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; 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; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; private final CustomAuthenticationProvider customAuthenticationProvider; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화 .sessionManagement( sm -> sm.sessionCreationPolicy( SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음, 요청은 JWT 인증 .formLogin(form -> form.disable()) // react에서 로그인 요청 관리 .httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용 .logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음 .authenticationProvider( customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용 .authorizeHttpRequests( auth -> auth // ADMIN만 접근 .requestMatchers("/api/test/admin") .hasRole("ADMIN") // ADMIN, LABELER 접근 .requestMatchers("/api/test/label") .hasAnyRole("ADMIN", "LABELER") // ADMIN, REVIEWER 접근 .requestMatchers("/api/test/review") .hasAnyRole("ADMIN", "REVIEWER") .requestMatchers(HttpMethod.OPTIONS, "/**") .permitAll() // preflight 허용 .requestMatchers( "/api/auth/signin", "/api/auth/refresh", "/swagger-ui/**", "/api/members/*/password", "/v3/api-docs/**") .permitAll() .anyRequest() .authenticated()) .addFilterBefore( jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter .class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장. ; return http.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } /** * CORS 설정 * * @return */ @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // CORS 객체 생성 config.setAllowedOriginPatterns(List.of("*")); // 도메인 허용 config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(List.of("*")); // 헤더요청 Authorization, Content-Type, X-Custom-Header config.setAllowCredentials(true); // 쿠키, Authorization 헤더, Bearer Token 등 자격증명 포함 요청을 허용할지 설정 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); /** "/**" → 모든 API 경로에 대해 이 CORS 규칙을 적용 /api/** 같이 특정 경로만 지정 가능. */ source.registerCorsConfiguration("/**", config); // CORS 정책을 등록 return source; } }