일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- asyncconfigurer
- java
- aws secretmanager
- java.util.list
- asynccustomautoconfiguration
- @FunctionalInterface
- micrometer tracing
- kotlin
- jpa
- traceId
- EnableWebMvc
- spring MVC
- CompletableFuture
- java list
- ResponseBody
- HashMap
- traceasynccustomautoconfiguration
- Spring JPA
- Sleuth
- SpringMVC
- map
- list
- b3-propagation
- spring
- DeferredImportSelector
- spring3 spring2 traceid
- java lambda
- awssecretsmanagerpropertysources
- Spring Boot
- elasticsearch
- Today
- Total
du.study기록공간
Spring을 사용하는 서버에서 CORS 설정 과정에서 발생한 이슈들 본문
신규 기능으로 엑셀 다운로드 기능을 개발하는 도중, CORS문제가 났던 문제를 기록해보려 합니다.
먼저 간략하게 구성도를 그려보면 브라우저(A.com)에서 B.com 으로 호출을 하고 B.com에선 nginx가 프록시 역할로 Excel 추출 서버로 토스해주는 구조로 서버를 구성했습니다. 서버들은 스프링,Undertow를 통해 구동시키고 있습니다.
첫번째 의문. 어째서 브라우저에서 POST로 호출을 했는데, OPTIONS로 변경이 되었는가.
요건 리서치를 통해서 빠르게 해결할 수 있었습니다.
CORS요청의 경우, 브라우저는 요청을 preflighted request로 변경하여 서버로 보내게 되는데, 먼저 Method를 OPTIONS로 호출을 하여 먼저 서버의 승인을 받고난 후, 먼저 요청한 Method로 요청 서버에 재 호출을 하게 됩니다.
좀더 자세한 내용을 참고할 페이지 : https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
두번째 의문. 어째서 스프링에선 계속해서 403 forbidden을 뱉어낼까..
CORS이슈를 보면서 너무 간단하게 Nginx에서 Access-Control-Allow-Origin 만 추가하면 되지않을까 했는데.. (오히려 더 망해버린 나쁜생각) 계속해서 뱉어대는 403에러를 해결하기 위해서 스프링 코드를 뜯어봤습니다.
결론적으로 코드를 통해보면 스프링 코드에서 preFlightRequest라는 것을 requestHeader를 조사하여 판단하게 됩니다.
그 결과 config 라는 애를 찾아오게 되는데, 요것이 null이면 rejectRequest를 통해 저 403!!!!을 뱉어내고 있었습니다.
//DefaultCorsProcessor
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
if (config == null) {
if (preFlightRequest) {
rejectRequest(serverResponse);
return false;
}
else {
return true;
}
}
//CorsUtil
public static boolean isCorsRequest(HttpServletRequest request) {
return (request.getHeader(HttpHeaders.ORIGIN) != null);
}
/**
* Returns {@code true} if the request is a valid CORS pre-flight one.
*/
public static boolean isPreFlightRequest(HttpServletRequest request) {
return (isCorsRequest(request) && HttpMethod.OPTIONS.matches(request.getMethod()) &&
request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
}
// DefaultCorsProcessor
protected void rejectRequest(ServerHttpResponse response) throws IOException {
response.setStatusCode(HttpStatus.FORBIDDEN);
response.getBody().write("Invalid CORS request".getBytes(StandardCharsets.UTF_8));
}
어휴 ... 우선 저 config를 null이 아닌 값을로 채워지기전에.. 일반적인 호출에서는 해당 코드를 타지않게 되는데, DispatcherServlet에서 getHandler를 하는 부분을 보면 해당 부분을 보면 다음과 같은 구문이 호출되게 됩니다.
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
해당부분에서 주목해야할 점은 if문 안인데, 위에서도 있던 CorsUtils.isCorsRequest 값을 통해서 excutionChain에 PreFlightHandler 가 추가되고 해당 핸들러를 시작으로 403을 뱉어내는 코드가 실행되게 됩니다. (ㅠ_ㅠ)
그리고 해당 블럭에서 중요한 config가 PreFlightHandler 에 set되게 되는데, globalConfig, handlerConfig 값을 통해서 config 설정이 됩니다.
쉽게 정리해보면
globalConfig 는 @EnableWebMvc 에서 설정하는 CorsMapping 매핑값이 set되게 됩니다.
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/stats/extration").allowedOrigins("http://무언가의도메인.com")
.allowedMethods(HttpMethod.GET.name(),HttpMethod.POST.name(),HttpMethod.OPTIONS.name());
}
handlerConfig 는 이름에서 유추되듯이 핸들러에서 어노테이션으로 설정한 값을 이용하여 handlerConfig에 set되게 됩니다.
@CrossOrigin(origins = {"http://무언가의 도메인.com"},allowCredentials = "true")
@RequestMapping(value = "/extraction", method = {RequestMethod.POST,RequestMethod.OPTIONS})
public StatisticsExcelView extractForumData(~~, ModelMap modelMap) {
// 코드부분
}
해당 값들을 통하여, config가 null피하게 되고, 결과적으로 403에러를 피하여 cors를 설정하였습니다 ㅠㅠ
추가 사항
nginx에서 The 'Access-Control-Allow-Origin' header contains multiple values 라는 에러를 접하게 되었는데.. 이 이유는 spring 에서 이미 * 한개를 내려주는데.. nginx에서 add_header 'Access-Control-Allow-Origin' '*'; 를 추가로 설정하여 multiValue가 된 슬픈이슈로 한참을 해맸습니다..
'스프링' 카테고리의 다른 글
Spring Boot 시작하기. (0) | 2020.03.17 |
---|---|
@Controller handlerMethod register (0) | 2020.03.07 |
Spring AOP - 관점 지향 프로그래밍 (0) | 2019.11.30 |
configureHandlerExceptionResolvers, extendHandlerExceptionResolvers 차이점 (0) | 2019.11.22 |
[Spring MVC] Exeption 처리방법 - @ControllerAdvice, @ExceptionHandler (1) | 2019.11.19 |