BackEnd/Spring

[Spring] CORS 문제 해결하기

샤아이인 2022. 4. 26.

1. CORS

1. CORS 란?

CORS는 Cross-Origin Resource Sharing 의 줄임말로, 다른 출처의 리소스를 공유하는 것 을 의미합니다.

즉, 다른 출처 간의 자원을 공유하는 정책이라고 생각하면 된다.

 

따로 CORS를 설정해주지 않는다면 해당 리소스를 공유하지 못하는 문제가 발생하게 됩니다.

크롬에서 위와같이 No 'Access-Control-Allow-Origin' 이라는 경고나 발생하고 있다!

 

2. 출처 란?

그럼 출처는 뭘 의미하는 것 일까? 예를 들어 www.google.com   같은 URL은 여러 개의 구성 요소로 이루어져 있다.

출처 - https://beomy.github.io/tech/browser/cors/

 

URL의 구성 요소 Protocol, Host, Path, Query String, Fragment 중에서 출처(Origin)는 Protocol, Host, Port 까지 합친 것을 의미한다.

즉, https://www.google.com:443 까지를 출처이다.

참고로 HTTP는 well-known 포트 번호는 80번이며, HTTPS는 443번으로 well-known 포트 번호이기 때문에 생략해도 된다.

 

3. 같은 출처? 다른 출처?

맨 처음 다른 출처간의 자원 공유로 인해 발생하는 문제라 언급한적이 있다.

그럼 같은 출처는 무엇이고? 다른 출처는 무엇일까?

 

나의 블로그 주소인 https://blogshine.tistory.com 를 기준으로 같은 출처와 다른 출처에 대하여 알아보자.

URL 같은 출처 이유
https://blogshine.tistory.com/category/BackEnd O 프로토콜, 호스트, 포트 번호 동일
https://blogshine.tistory.com/category/BackEnd?param=1 O 프로토콜, 호스트, 포트 번호 동일
https://another.tistory.com/category/BackEnd X 호스트가 다름
https://www.google.com X 호스트가 다름
http://blogshine.tistory.com/category/BackEnd X 프로토콜이 다름 (http)

위에서 말한것 처럼 출처(Origin)는 Protocol, Host, Port 까지 합친 것을 의미한다.

이를 기준으로 URL을 살펴보면 쉽게 다른 출처에 대하여 인식할 수 있다.

 

4. SOP 란?

SOP는 Same-Origin Policy의 줄임말로, 단어 뜻 그대로 같은 출처만 허용한다는 정책 입니다.

과거에는 보안을 위해 엄격하게 같은 출처만 통신하도록 허용하였으나, 최근에는 다른 출처에 있는 리소스를 가져와서 사용하는 일이 아주 흔하므로 SOP의 예외 조항인 CORS 정책을 두게 되었다.

 

5. CORS 동작 과정

CORS의 동작 방식은 기본적으로 Web-Client가 다른 출처의 resource를 요청할 때, HTTP 프로토콜을 사용하여 요청합니다.

이때 브라우저는 request Header의 Origin 이라는 필드에 요청을 보내는 출처를 함께 담는다.

 

예를 들어 Origin: https://www.google.com 과 같이 출처를 담아서 서버로 보낸다고 합시다.

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://www.google.com

Server는 response Header의 Access-Control-Allow-Origin이라는 값에 이 리소스를 접근하는 것이 허용된 출처 목록을 담아줍니다.

 

이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교하게 되고,

만약 허용되지 않는 Origin이면 CORS 정책 위반 이슈가 발생하게 됩니다.

(주의! CORS 이슈가 발생해도 서버의 응답은 200번이 온다.)

 

CORS의 동작 방식으로는 크게 2가지가 있습니다.

1. 단순 요청 방식 (Simple Request)

2. 예비 요청 방식 (Preflight Request)

 

5 - 1) Simple Request

Simple Request 방식은 예비 요청을 보내지 않고 서버에게 바로 본 요청을 보낸 후, 응답 헤더의 Access-Control-Allow-Origin 값을 확인하여 CORS 정책 위반 여부를 확인하는 방식 입니다.

출처 - https://beomy.github.io/tech/browser/cors/

Simple Request 방식은 다음 조건을 만족할때만 사용할 수 있습니다.

  1. 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안 된다.
  3. Content-Type의 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 가능하다.

 

1번 조건은 지킬 수 있어도 2, 3번 조건이 지키기가 어려워 사용되기 어렵다.

3번만 해도 보통 application/json을 많이 사용하기 때문에 적용시키기 어렵다.

 

5 - 2) Preflight Request

Preflight Request는 일반적인 방식으로, 브라우저가 요청을 한 번에 보내지 않고 예비 요청과 본 요청으로 나누어서 서버로 전송한다.

Preflight 는 실제 리소스를 요청하기 전에 OPTIONS라는 메서드를 통해 실제 요청을 전송할지 판단하게 됩니다.

출처 -https://beomy.github.io/tech/browser/cors/

서버는 예비 요청에 대한 응답으로 Access-Control-Allow-Origin 헤더를 포함한 response를 브라우저에 보냅니다.

브라우저는 단순 요청과 동일하게 response로 온 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단합니다.

 

즉, OPTIONS를 통해 브라우저 스스로 이 요청이 안전한지 확인하는 역할을 한다.

 

 

2. CORS 해결하기

일반적으로 나같은 경우 FE와 협업을 하던 도중 발생하는 문제였다.

 

Spring에서 CORS를 Global 하게 적용하는 방식과, 특정 Controller에 적용하는 방법을 알아보자.

 

1. WebMvcConfigurer 를 통해 해결하기

이 방식이 Global 하게 적용되는 방식이다. 이 방식을 난 주로 사용했었다.

 

다음과 같은 Config 파일을 하나 만들어 줍시다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POSP");
}

클래스 상단에 @Configuration 어노테이션을 통해 설정파일이라는 것을 알려줍니다.

그리고 WebMvcConfigurer를 implements합니다.

 

이후 registry를 통해 허용항 Origins 와 Methods를 지정해주면 됩니다.

 

2. @CrossOrigin annotation 적용하기

이 방식은 Controller 또는 메소드단에서 annotation을 통해 적용하는 방법입니다.

@RequestMapping("/path")
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class ApiController {
}

위처럼 CrossOrigin이라는 어노테이션을 사용하면, global하게 설정하던것 처럼 origins이나 methods를 지정할 수 있습니다.

지정 가능한 옵션으로는 origins, methods, maxAge, allowedHeaders가 있습니다.

 

아니면 Controller 내부의 특정 메서드에만 적용시킬수도 있습니다!

 

3. 출처

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

developer.mozilla.org

https://beomy.github.io/tech/browser/cors/

 

[Browser] CORS란?

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)에 대해 살펴보도록 하겠습니다.

beomy.github.io

 

댓글