스프링에서 CORS 설정하기

junto·2024년 6월 16일
0

spring

목록 보기
16/30
post-thumbnail

Nginx에서 CORS 설정을 할 수도 있지만, Spring에서 하는 방법을 알아본다!

CORS(Cross Origin Resource Sharing)

  • 프론트앤드와 협업하게 되면 자주 만나게 되는 문제이다. 배포단계에선 앞단에 nginx로 라우팅하게 되면 둘 다 동일 출처로 보기에 CORS 설정이 필요 없다. 하지만 개발 단계에서는 필요한 경우가 많다. 백엔드 서버(ex: localhost:8080)와 프론트 서버(ex: localhost:3000)는 다른 출처를 가지기 때문이다. 그럼 왜 교차 출처 허용(CORS)이 필요할까?

보안을 위한 동일 출처 정책

  • 잠재적인 보안 취약점을 예방하기 위해 필요하다고 한다. 예를 들어 악성 웹사이트에서 JavaScript를 이용해 사용자가 로그인한 다른 웹사이트의 데이터를 조작하는 것을 방지하기 위해서다.
  • 즉, 동일 출처가 아니면 다른 출처에서 데이터를 조작할 수 없게 된다.

동일 출처로 보는 기준

  • 프로토콜(protocol, scheme), 포트, 호스트(host, domain)가 모두 동일한 경우 동일 출처로 본다.
  • SecurityCorsFilter에서 이를 찾아보면 아래와 같이 동일 출처인지 판단하는 것을 확인할 수 있다.
return !(ObjectUtils.nullSafeEquals(scheme, originUrl.getScheme()) &&
				ObjectUtils.nullSafeEquals(host, originUrl.getHost()) &&
				getPort(scheme, port) == getPort(originUrl.getScheme(), originUrl.getPort()));

Spring에서 다른 출처를 허용하는 방법

1. WebMvc에서 다른 출처 허용하기

@Configuration
public class CorsMvcConfig implements WebMvcConfigurer {

  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**").allowedHeaders("*").allowedOrigins("http://localhost:5173").allowedMethods("*");
  }
}

2. SecurityFilter에서 다른 출처 허용하기

public SecurityFilterChain filterChain(HttpSecurity http, JwtUtil jwtUtil) throws Exception {

    http.cors(
        (cors) ->
            cors.configurationSource(
                request -> {
                  CorsConfiguration config = new CorsConfiguration();

                  config.setAllowedHeaders(Collections.singletonList("*"));
                  config.setAllowedOrigins(
                      List.of("http://localhost:5173"));
                  config.setAllowedMethods(Collections.singletonList("*"));

                  return config;
                }));
	 ...
}

3. Intercepter에서 다른 출처 허용하기

  • 이론상 가능하다. 아래 preHandle 메서드를 이용해서 preflight 요청에서 허용할 헤더와 Options 메서드를 허용할 수 있어보인다.
public interface HandlerInterceptor {
  default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return true;
  }

  default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
  }

  default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
  }
}
  • 하지만, 굳이 이렇게 작성할 필요가 없다. WebMvcConfigurationSupport 에서 등록된 인터셉터와 cors 설정을 추가하기 때문이다.
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
  ...
  private void initHandlerMapping() {
    ...
    mapping.setInterceptors(this.getInterceptors(conversionService, resourceUrlProvider));
    mapping.setCorsConfigurations(this.getCorsConfigurations());
  }
}

그림으로 호출 순서 알아보기 (2 → 1 → 3)

호출 스택으로 적용 순서 알아보기

  • WAS(tomcat)은 요청을 필터와 서블릿을 관리한다.

1. WebMvcAutoConfiguration (자동 설정)

// 1. WebMvcAutoConfiguration (자동 설정)
@AutoConfigureOrder(-2147483638)
@ImportRuntimeHints({WebResourcesRuntimeHints.class})
public class WebMvcAutoConfiguration {
}

2. DeletegatingWebMvcConfiguration에서 setConfigurers로 cors 설정

public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
}
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
	...
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
  if (!CollectionUtils.isEmpty(configurers)) {
    this.configurers.addWebMvcConfigurers(configurers);
  }
}

3. WebMvcConfigurationSupport에서 Interceptor 설정하고, Cors 설정 불러오기

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
  ...
  private void initHandlerMapping() {
    ...
    mapping.setInterceptors(this.getInterceptors(conversionService, resourceUrlProvider));
    mapping.setCorsConfigurations(this.getCorsConfigurations());
  }
}

참고 자료

profile
꾸준하게

0개의 댓글