diff --git a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java new file mode 100644 index 00000000..c113f156 --- /dev/null +++ b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java @@ -0,0 +1,73 @@ +package com.moabam.global.auth.filter; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import com.google.cloud.storage.HttpMethod; +import com.moabam.global.error.exception.UnauthorizedException; +import com.moabam.global.error.model.ErrorMessage; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Order(0) +@Component +@RequiredArgsConstructor +public class CorsFilter extends OncePerRequestFilter { + + private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; + private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " + + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; + + private final HandlerExceptionResolver handlerExceptionResolver; + + @Value("${allow}") + private String allowOrigin; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + + try { + if (!secureMatch(httpServletRequest, allowOrigin)) { + throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); + } + } catch (UnauthorizedException unauthorizedException) { + log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOrigin); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + httpServletResponse.setHeader("Access-Control-Allow-Origin", allowOrigin); + httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); + httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); + + if (isOption(httpServletRequest.getMethod())) { + httpServletRequest.setAttribute("isPermit", true); + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + public boolean secureMatch(HttpServletRequest request, String origin) { + return request.getHeader("referer").contains(origin); + } + + public boolean isOption(String method) { + return HttpMethod.OPTIONS.name().equals(method); + } +} diff --git a/src/main/java/com/moabam/global/config/WebConfig.java b/src/main/java/com/moabam/global/config/WebConfig.java index b72e98af..387714cb 100644 --- a/src/main/java/com/moabam/global/config/WebConfig.java +++ b/src/main/java/com/moabam/global/config/WebConfig.java @@ -2,13 +2,11 @@ import java.util.List; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.moabam.api.application.auth.mapper.PathMapper; @@ -19,22 +17,6 @@ @EnableScheduling public class WebConfig implements WebMvcConfigurer { - private static final String ALLOWED_METHOD_NAMES = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,PATCH"; - private static final String ALLOW_ORIGIN_PATTERN = "[a-z]+\\.moabam.com"; - - @Value("${allow}") - private String allowLocalHost; - - @Override - public void addCorsMappings(final CorsRegistry registry) { - registry.addMapping("/**") - .allowedOriginPatterns(ALLOW_ORIGIN_PATTERN, allowLocalHost) - .allowedMethods(ALLOWED_METHOD_NAMES.split(",")) - .allowedHeaders("*") - .allowCredentials(true) - .maxAge(3600); - } - @Override public void addArgumentResolvers(List resolvers) { resolvers.add(handlerMethodArgumentResolver()); diff --git a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java index fa304209..0f5d04d7 100644 --- a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java @@ -1,10 +1,16 @@ package com.moabam.api.presentation; -import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; -import static org.springframework.test.web.client.response.MockRestResponseCreators.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.mockito.BDDMockito.any; +import static org.mockito.BDDMockito.doReturn; +import static org.mockito.BDDMockito.willReturn; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -37,6 +43,7 @@ import com.moabam.api.dto.auth.AuthorizationCodeResponse; import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.global.auth.filter.CorsFilter; import com.moabam.global.common.util.GlobalConstant; import com.moabam.global.config.OAuthConfig; import com.moabam.global.error.handler.RestTemplateResponseHandler; @@ -58,6 +65,9 @@ class MemberAuthorizeControllerTest { @SpyBean AuthorizationService authorizationService; + @SpyBean + CorsFilter corsFilter; + @Autowired OAuthConfig oAuthConfig; @@ -77,6 +87,7 @@ void setUp() { RestTemplate restTemplate = restTemplateBuilder.build(); ReflectionTestUtils.setField(oAuth2AuthorizationServerRequestService, "restTemplate", restTemplate); mockRestServiceServer = MockRestServiceServer.createServer(restTemplate); + willReturn(true).given(corsFilter).secureMatch(any(), any()); } @DisplayName("인가 코드 받기 위한 로그인 페이지 요청") diff --git a/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java index d4d0e8eb..31f6303a 100644 --- a/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java +++ b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java @@ -139,7 +139,7 @@ void filter_have_any_refresh_token_error() throws ServletException, IOException eq(null), any(UnauthorizedException.class)); } - @DisplayName("새로운 도큰 발급 성공") + @DisplayName("새로운 토큰 발급 성공") @Test void issue_new_token_success() throws ServletException, IOException { // given diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index 9645716a..ae2b827a 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -1,15 +1,18 @@ package com.moabam.support.common; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willReturn; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; import com.moabam.api.domain.member.Role; +import com.moabam.global.auth.filter.CorsFilter; import com.moabam.global.auth.handler.PathResolver; @ExtendWith({FilterProcessExtension.class}) @@ -18,8 +21,17 @@ public class WithoutFilterSupporter { @MockBean private PathResolver pathResolver; + @MockBean + private DefaultHandlerExceptionResolver handlerExceptionResolver; + + @SpyBean + private CorsFilter corsFilter; + @BeforeEach void setUpMock() { + willReturn(true) + .given(corsFilter).secureMatch(any(), any()); + willReturn(Optional.of(PathResolver.Path.builder() .uri("/") .role(Role.USER)