Spring CORS 에러 해결

12431 단어 corsSpringSpring

Spring CORS 에러 해결 방법

  • 개요 : node axios를 통해서 클라이언트와 통신하던 중 CORS 문제와 마주쳤다. 이를 해결하기위해 서칭하던 중 적어둘 필요가 있어서 적어둔다.

1. CORS(Cross-Origin Resource Sharing) policy란?

출처

  • CORS 정책을 위반할 때 발생한다.
  • 브라우저에서 다른 출처의 리소스를 공유하는 방법에서의 정책을 위반할 시 발생한다.

1-1. 다른 출처의 리소스란?

  • URL 구조

  • 출처(Origin)이란?

    • 출처란 URL 구조에서 살펴본 Protocol, Host, Port를 합친것을 의미한다.
  • 동일 출처 정책(SOP: Same-Origin Policy)란?

    • 브라우저에서 API 호출할때만 CORS policy를 만나는 이유는 브라우저가 동일 출처 정책을 지켜서 그렇다.
    • SOP란 다른 출처의 리소스 접근을 금지하는 정책이다.
    • 예를 들어서 beomy.github.io라는 도메인 주소를 사용중인 웹페이지에서 beomy-api.github.io라는 API 서버로 데이터를 요청해 화면을 그리면 해당 웹페이지는 동일 출저 정책(SOP)를 위반한 것이다.
      • 장점 : XSSXSRF등의 보안 취약점을 노린 공격을 방어할 수 있다.
    • 현실적으로 외부 리소스를 참고하는것이 필요하기에 외부 리소스를 가져올 수 있는 방법이 존재해야 한다.
    • 외부 리소스를 사용하기 위한 SOP 예외 조항이 CORS이다.

1-2. CORS 동작원리

  • simple request

    • 단순 요청은 서버에 API를 요청하고, 서버는 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단합니다.

    • simple request 조건 : 서버로 전달하는 요청(request)이 아래의 3가지 조건을 만족해야 서버로 전달하는 요청이 단순 요청으로 동작한다.

      • 요청 메서드(method)는 GET, HEAD, POST 중 하나여야 합니다.
      • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안 됩니다.
      • Content-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나를 사용해야 합니다
      • 첫 번째 조건은 어렵지 않은 조건이지만 2번, 3번 조건은 까다로운 조건입니다. 2번 조건은 사용자 인증에 사용되는 Authorization 헤더도 포함되지 않아 까다로운 조건이며, 3번 조건은 많은 REST API들이 Content-Type으로 application/json을 사용하기 때문에 지켜지기 어려운 조건입니다.
  • preflight request

    • Preflight 요청은 서버에 예비 요청을 보내서 안전한지 판단한 후 본 요청을 보내는 방법.
    • Preflight 요청은 실제 리소스를 요청하기 전에 OPTIONS라는 메서드를 통해 실제 요청을 전송할지 판단한다.
    • OPTIONS 메서드로 서버에 예비 요청을 먼저 보내고, 서버는 이 예비 요청에 대한 응답으로 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 단순 요청과 동일하게 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단한다.
  • CORS 해결 방법

    • Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보내는 방식으로 CORS 에러를 해결할 수 있다.

2. Spring CORS 해결 방법

출처

  1. CorsFilter 클래스 생성

    • filter 패키지를 만들고, CorsFilter 클래스를 생성한다. (패키지 위치는 자유)
  2. 다음의 코드를 넣는다.

    package com.test.blog.filter;
    
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class CorsFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            response.setHeader("Access-Control-Allow-Origin", "http://localhost:8090");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods","*");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers",
                    "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    
            if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            }else {
                chain.doFilter(req, res);
            }
        }
    
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }
    

좋은 웹페이지 즐겨찾기