NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    Java Java Spring boot에서 CORS(Cross-Origin Resource Sharing) 적용하는 방법.

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 웹프로젝트를 진행하다보면 CORS 문제가 가끔 발생합니다. 웹브라우저 플랫폼을 사용하면 필연적인 문제이긴 합니다. 웹브라우저는 보안적인 이유로 Cross-Origin HTTP(HyperText Transfer Protocol) 요청들을 제한합니다. 그래서 Cross-Origin 요청을 하려면 서버의 동의가 필요합니다. 서버가 동의하면 브라우저는 요청을 허락합니다. 만약, 동의하지 않는다면 브라우저에서 거절하고 사용자에게 CORS 에러를 표시합니다. 허락을 구하고 거절하는 메커니즘은 HTTP-Header를 이용해서 처리하는데요. 이를 CORS(Cross-Origin Resource Sharing)라고 부릅니다.

    ixhPfjm.png

     

     

    Cross-Origin이 발생하는 원인은 아래와 같은 3가지 경우입니다.

    • HTTP와 HTTPS는 다른 프로토콜입니다.
    • domain.com과 other-domain.com은 다른 도메인입니다.
    • 80포트와 8080포트는 다릅니다.

     

    그럼, CORS는 왜 필요할까요? 개발하다보면 불편한데 말이죠. 만약, CORS가 없이 모든 곳에서 데이터를 요청할 수 있게되면 보안상 문제가 발생하게 됩니다. 이를 설명하기 위해서는 SOP(Same Origin Policy)에 대해 약간의 이해가 필요합니다. SOP는 동일 출처 정책이라는 의미로 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 보안 방식입니다. 다시 말해서 SOP는 두 URL의 프로토콜, 호스트, 포트가 모두 같아야 동일한 출처로 인정되며 웹사이트를 샌드박스화하여 잠재먹인 보안 위협으로부터 보호해주는 정책입니다. 이렇다보니 개발할 때 이 문제를 자주 겪게 됩니다. 물론, 혼자서 개발할 때는 문제가 없습니다. Backend, Frontend 개발 협업에서 자주 발생합니다.

    VF7x4We.png

     

     

    SOP가 없는 상황에서 악의적인 코드가 포함된 JavaScript가 있다고 생각 해봅시다. 사용자가 악성 페이지에 접속하면, 악의적인 자바스크립트가 자동으로 실행됩니다. 사용자가 모르는 사이에 네이버나 구글에 임의의 요청을 보내고 그 응답 값은 해커의 서버로 보내서 정보를 가로챌 수 있습니다. 일반적으로 웹브라우저는 윈도우 시스템상 관리자 권한으로 실행되기 때문에 자바스크립트를 이용해서 사용자가 접속중인 내부망의 아이피와 포트를 스캐닝하거나 사용자의 브라우저를 프록시처럼 사용할수도 있습니다. 하지만, 웹이라는 오픈 스페이스 환경에서 다른 출처에 있는 리소스를 사용하는 일은 매우 흔하기 때문에 이를 무작정 막을수도 없습니다. 거대 포털 사이트의 경우 호스트를 다르게해서 서비스하는 것을 알 수 있습니다. blog.naver.com, cafe.naver.com등등... 말이죠.

     

    이와같은 문제를 우회하기 위해서 SOP에는 예외 조항이 있습니다. CORS 정책을 지킨 요청은 출처가 다르더라도 리소스 공유를 허용하기로 말입니다. 스프링부트 또는 nodeJS는 CORS(Cross-Origin Resource Sharing)를 설정할 수 있는 기능들이 포함되어 있습니다. 아래는 Java Spring-boot에서 CORS를 처리하는 방법입니다. 공통 구성(Common/Config) 폴더에 CrossOriginFilterConfig.java 파일을 하나 생성합니다.

    81zb2R7.png

     

     

    아래와 같이 코드를 작성합니다.

    package com.ngm.editor7.common.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import jakarta.servlet.Filter;
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.FilterConfig;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.ServletRequest;
    import jakarta.servlet.ServletResponse;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    @Component
    @Order(Ordered.LOWEST_PRECEDENCE)
    public class CrossOriginFilterConfig implements Filter {
    
        @Value("${cors.allow.origins:*}")
    	private String allowOrigins;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @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", allowOrigins);
            response.setHeader("Access-Control-Allow-Credentials", "false");
            response.setHeader("Access-Control-Allow-Methods","GET, POST");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "*");
    
            if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            }else {
                chain.doFilter(req, res);
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }

     

    요청 헤더 목록

    • Access-Control-Request-Method
      - preflight 요청을 할 때 실제 요청에서 어떤 메서드를 사용할 것인지 서버에게 알리기 위해 사용됩니다.
    • Access-Control-Request-Headers
      - preflight요청을 할 때 실제 요청에서 어떤 header를 사용할 것인지 서버에게 알리기 위해 사용됩니다.

     

    응답 헤더 목록

    • Access-Control-Allow-Origin
      - 브라우저가 해당 origin이 자원에 접근할 수 있도록 허용합니다. 혹은 *은 credentials이 없는 요청에 한해서 모든 origin에서 접근이 가능하도록 허용합니다.
    • Access-Control-Expose-Headers
      - 브라우저가 액세스할 수 있는 서버 화이트리스트 헤더를 허용합니다.
    • Access-Control-Max-Age
      - 얼마나 오랫동안 preflight요청이 캐싱 될 수 있는지를 나타낸다.
    • Access-Control-Allow-Credentials
      - Credentials가 true 일 때 요청에 대한 응답이 노출될 수 있는지를 나타냅니다.
      - preflight요청에 대한 응답의 일부로 사용되는 경우 실제 자격 증명을 사용하여 실제 요청을 수행할 수 있는지를 나타냅니다.
      - 간단한 GET 요청은 preflight되지 않으므로 자격 증명이 있는 리소스를 요청하면 헤더가 리소스와 함께 반환되지 않으면 브라우저에서 응답을 무시하고 웹 콘텐츠로 반환하지 않습니다.
    • Access-Control-Allow-Methods
      - preflight`요청에 대한 대한 응답으로 허용되는 메서드들을 나타냅니다.
    • Access-Control-Allow-Headers
      - preflight요청에 대한 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP 헤더를 나타냅니다.

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.