diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index 77a8e5b..a5c3aaa 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -22,6 +22,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; @Configuration @EnableWebSecurity @@ -41,7 +44,7 @@ public SecurityConfig(UserService userService, OAuth2AuthorizedClientService aut public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests - .requestMatchers("/", "/question/**", "/login").permitAll() + .requestMatchers("*", "/question/**", "/login").permitAll() // "*"에 대한 설정은 CorsTest를 위함 .anyRequest().authenticated() ) .oauth2Login(oauth2Login -> oauth2Login @@ -60,7 +63,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // 세션을 생성하지 않도록 설정 .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // JWT 필터 추가 - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .addFilter(corsFilter()); return http.build(); } @@ -97,8 +101,8 @@ public AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler() { public LogoutHandler logoutHandler() { return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { // 쿠키 삭제 - deleteCookie(response, "accessToken"); - deleteCookie(response, "refreshToken"); + deleteCookie(response, "access_token"); + deleteCookie(response, "refresh_token"); }; } @@ -118,4 +122,21 @@ private void deleteCookie(HttpServletResponse response, String cookieName) { cookie.setSecure(true); response.addCookie(cookie); } + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 + configuration.addAllowedMethod("GET"); + configuration.addAllowedMethod("POST"); + configuration.addAllowedMethod("PUT"); + configuration.addAllowedMethod("PATCH"); + configuration.addAllowedMethod("DELETE"); + configuration.addAllowedHeader("*"); // 모든 헤더 허용 + configuration.setAllowCredentials(true); // 인증 정보 포함 허용 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); // 모든 경로에 대해 CORS 설정 적용 + return new CorsFilter(source); + } } \ No newline at end of file diff --git a/src/test/java/com/mtvs/devlinkbackend/config/CorsRequestTest.java b/src/test/java/com/mtvs/devlinkbackend/config/CorsRequestTest.java new file mode 100644 index 0000000..33142b2 --- /dev/null +++ b/src/test/java/com/mtvs/devlinkbackend/config/CorsRequestTest.java @@ -0,0 +1,37 @@ +package com.mtvs.devlinkbackend.config; + +import jakarta.persistence.Id; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import static org.springframework.http.HttpHeaders.*; + +@SpringBootTest +@AutoConfigureMockMvc +public class CorsRequestTest { + @Autowired + private MockMvc mockMvc; + + @Test + public void testCorsConfig() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.options("/api/some-endpoint") + .header(ORIGIN, "http://localhost:5173") + .header(ACCESS_CONTROL_REQUEST_METHOD, "GET")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.header().string(ACCESS_CONTROL_ALLOW_ORIGIN, "http://localhost:5173")) + .andExpect(MockMvcResultMatchers.header().string(ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,PUT,DELETE")); + } + + @Test + public void testCorsConfig_withDisallowedOrigin() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.options("/api/some-endpoint") + .header(ORIGIN, "http://disallowed-origin.com") + .header(ACCESS_CONTROL_REQUEST_METHOD, "GET")) + .andExpect(MockMvcResultMatchers.status().isForbidden()); // 허용되지 않은 Origin인 경우 + } +}