package com.example.springjwt.config;
 
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@Configuration
public class CorsMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
                .allowedOrigins(
                        "https://spb.abc.com",
                        "http://localhost:3000"
                ) // 정확한 출처 지정
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 메서드 명확히 설정
                .allowCredentials(true) // 인증 정보 포함 허용
                .allowedHeaders("Authorization", "Content-Type", "Cache-Control");
    }
}
 
/**
 * 이 설정을 유지하려면 SecurityConfig.java에서 .cors() 설정을 제거해야 한다.
 * (권장) WebMvcConfigurer를 삭제하고, SecurityConfig.java에서 CORS를 관리하는 것이 좋다.
 */
 
package com.example.springjwt.config;
 
import com.example.springjwt.jwt.CustomLogoutFilter;
import com.example.springjwt.jwt.JWTFilter;
import com.example.springjwt.jwt.JWTUtil;
import com.example.springjwt.jwt.LoginFilter;
import com.example.springjwt.repository.RefreshRepository;
import com.example.springjwt.service.RSAService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
 
import java.util.Arrays;
import java.util.Collections;
 
@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
    // AuthenticationManager 가 인자로 받을 AuthenticationConfiguraion 객체 생성자 주입
    private final AuthenticationConfiguration authenticationConfiguration;
    private final JWTUtil jwtUtil;
    private final RefreshRepository refreshRepository;
    private final RSAService rsaService;
 
    public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil, RefreshRepository refreshRepository, RSAService rsaService) {
 
        this.authenticationConfiguration = authenticationConfiguration;
        this.jwtUtil = jwtUtil;
        this.refreshRepository = refreshRepository;
        this.rsaService = rsaService;
    }
 
    //AuthenticationManager Bean 등록
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
 
        return configuration.getAuthenticationManager();
    }
 
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
 
        http
                .cors((corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
 
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
 
                        CorsConfiguration configuration = new CorsConfiguration();
                        configuration.setAllowedOriginPatterns(Arrays.asList(
                                "https://spb.abc.com",
                                "http://localhost:3000"
                        )); // allowedOrigins("*")을 사용하지 않고, 구체적인 출처를 명시
                        configuration.setAllowCredentials(true);
 
                        // allowedMethods("*") 대신 GET, POST, PUT, DELETE, OPTIONS를 명확하게 설정
                        // configuration.setAllowedMethods(Collections.singletonList("*"));
                        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
 
                        // allowedHeaders("*") 대신 Authorization, Content-Type, Cache-Control을 지정
                        // configuration.setAllowedHeaders(Collections.singletonList("*"));
                        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "Cache-Control"));
 
                        configuration.setExposedHeaders(Collections.singletonList("Authorization"));
 
                        configuration.setMaxAge(3600L);
 
                        return configuration;
                    }
                })));
 
        //csrf disable
        http
                .csrf((auth) -> auth.disable());
 
        //From 로그인 방식 disable
        http
                .formLogin((auth) -> auth.disable());
 
        //http basic 인증 방식 disable
        http
                .httpBasic((auth) -> auth.disable());
 
 
        //경로별 인가 작업
        http
                .authorizeHttpRequests((auth) -> auth
                        .requestMatchers("/api/join", "/login", "/", "/api/public-key", "/api/pub-key").permitAll()
                        .requestMatchers("/admin").hasRole("ADMIN")
                        .requestMatchers("/reissue").permitAll()
                        .anyRequest().authenticated());
 
        http
                .addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class);
 
        http
                .addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, refreshRepository, rsaService), UsernamePasswordAuthenticationFilter.class);
 
        http
                .addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class);
        
        //세션 설정
        http
                .sessionManagement((session) -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
 
        http
                .csrf((auth) -> auth.disable());
 
        return http.build();
    }
}