이번 프로젝트는 프론트엔드와 협업하고 있어서 CORS설정을 할 필요가 있었다. 간단하게 CORS에 대해 공부한 것과 spring에서 CORS 설정 하는 방법에 대해서 정리해보려고 한다.

 

CORS (Cross Origin Resource Sharing)

브라우저는 보안 상의 이유로 도메인 외부의 자원은 허용한 출처에 한해서만 자원을 사용할 수 있도록 하는 정책을 말한다.

 

SOP(Same Origin Policy)

동일한 출처에 대해서만 리소스를 공유하는 정책을 말한다.

 

- 일반적으로 출처란 Protocol + Host + port 를 의미하며 출처가 동일하다는 것은 Protocal, Host, port가 모두 같은 경우를 말한다. (브라우저별로 동일한 출처에 대한 기준이 다르지만 Internet Explorer를 제외하고는 거의 모든 브라우저가 Protocal, Host, port 가 같아야 동일 출처라고 판단한다.)

 

CORS 문제가 생기는 이유

HTTP 요청에 대해서 어떤 요청을 하느냐에 따라서 CORS또는 SOP를 따르는 다른 정책을 가지고 있다.

  • HTML → 기본적으로 CORS
  • XMLHttpRequest, Fetch API 등 → SOP
    • SOP정책을 사용하는 경우 서로 다른 도메인에 대한 요청을 보안상 제한하기 때문에 CORS를 위한 응답 헤더를 통해 다른 도메인에 대한 요청을 허용하도록 해야 함

 

CORS 동작 방식

  • Preflight Request  
    • 브라우저에서 서버로 예비 요청을 보낸다.
    • 예비 요청이 완료되면 본 요청을 서버로 보낸다.
    • 대부분의 경우 프리플라이트 방식을 사용한다.

Preflight Request

 

  • Simple Request 
    • 예비 요청 없이 본 요청을 바로 보내는 것
    • 본 요청만 보내기 위한 조건이 까다로움
      • 요청 메소드는 GET, HEAD, POST
      • Accept, Accept-Language, Content-Language, Conetent-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 를 제외한 헤더 사용 금지
      • Content-Type은 application/x-www-form-urlencodedmultipart/form-datatext/plain 만 허용
    • 까다로운 조건, json 사용이 불가능한 단점이 있기 때문에 사용하기 쉽지 않음
    • ‼️Simple Request 조건이 만족하면 Simple Request로 동작하고 조건을 만족하지 못한다면 preflight Request 로 동작하게 된다.

Simple Request

 

  • Credentialed Request
    • 인증된 요청을 사용하는 방법으로 다른 출처 간 통신에서 보안을 강화하고 싶을 때 사용하는 방법
    • XMLHttpRequest, fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더 정보를 담지 않는다.
      • credentials 옵션을 통해 인증과 관련된 정보를 담을 수 있게 해줄 수 있음
        • same-origin (default) → 같은 출처 간 요청에만 인증 정보를 담게 함
        • include → 모든 요청에 인증 정보를 담게 함
        • omit → 모든 요청에 인증 정보를 담지 않게 함
    • credentials 에서 include option 사용 시 Access-Control-Allow-Origin 헤더에 * 사용 불가능
    • Access-Control-Allow-Credentials: true 헤더가 응답에 포함되어 있어야 함

 

 

Spring에서 CORS 해결 방법

1. WebMvcConfiguerer를 상속하여 addCorsMappings 메서드를 구현한 클래스를 Bean으로 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000", "http://localhost:80")
                .allowedMethods("GET", "POST", "PATCH", "DELETE")
                .allowCredentials(true)
                .maxAge(3000);
}

2. Interceptor에서 Header 직접 추가 후 interceptor 등록

@Component
public class CorsInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, origin, content-type, accept");

        return true;
    }
}

 

 

 

참고 자료

https://evan-moon.github.io/2020/05/21/about-cors/

+ Recent posts