CORS란?
- Cross Origin Resource Sharing
- 다른 출처의 자원을 공유하는 것.
- Spring Cloud에서는 아무것도 설정하지 않으면 보안적인 이유로 다른 출처의 HTTP 요청을 제한하는 정책을 사용하고 있다. (SOP; Same Origin Policy)
- 하지만 Frontend, Backend 서버가 나눠져 있는 경우 등 외부에서 요청을 하게 되는 일이 발생한다. 이 때 CORS 정책을 설정해줌으로서 다른 출처의 자원에 접근할 수 있도록 하는 것이다.
- MOZILLA에서는 CORS에 대해 다음과 같이 설명하고 있다
Cross Origin Resource Sharing은 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 체제이다.
Cross-Origin이란?
- 프로토콜이 다른 경우 (http/https)
- 도메인이 다른 경우
- 포트번호가 다른 경우
왜 필요할까?
- 모든 곳에서 자원 접근이 가능하면 (데이터를 요청해서 받아갈 수 있다면) 다른 사이트에서 원래 사이트를 흉내낼 수 있기 때문에 피싱사이트를 만들어 사용자의 정보를 빼내는 등 악용할 가능성이 있다.
- 공격을 막기 위해 필요한 경우에만 서버와 협의하여 요청할 수 있도록 하는 것이 CORS 이다.
CORS가 동작하는 3가지 시나리오
1. 단순요청 (Simple Request)
- preflight 요청 없이 바로 요청을 보낸다
- 아래에 명시된 메서드, Content-type, 헤더만 가능하다
- 메서드: GET, POST, HEAD
- Content-type
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 헤더: Accept, Accept-Language, Content-Language, Content-Type 만 허용
2. 사전 요청 (Preflight Request)
- OPTIONS메서드를 통해 다른 도메인 리소스에 요청이 가능한지 실제 요청 전에 미리 요청을 보내는 것. Preflight 요청으로 요청이 가능한 것을 확인하게 되면 실제 요청을 보낸다
- Request에 들어갈 것
- Origin : 요청 출처
- Access-Control-Request-Method: 실제 요청의 메서드
- Access-Control-Headers: 실제 요청의 추가 헤더
- Response에 들어갈 것
- Access-Control-Allow-Origin: 허가 출처
- Access-Control-Allow-Methods: 허가 메서드
- Access-Control-Allow-Headers: 허가 헤더
- Access-Control-Max-Age: Preflight 응답 캐시 시간
Spring Cloud Gateway에서의 CORS 설정
- Spring Cloud Gateway의 공식문서에서 제공하는 CORS 설정 방법 (바로가기)
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://front-end-domain.com"
allowedMethods:
- GET
- POST
하지만 이렇게 하면 다음과 같이 에러가 난다 🤦♀️
원인 및 해결
1. Preflight
- 원인
- Spring Cloud Gateway에서 사용하는 CORS 필터는 preflight 요청일 때(OPTIONS메서드) 뒷단 서버로 보내지 않고 바로 리턴시켜 버린다.
- 때문에 응답에 접근 허용 헤더(Access-Control-Allow-Origin)
를 받지 못해 서버가 허용하지 않는 요청이라고 판단하여 CORS 에러가 발생한 것이다.
- 해결
- allowedMethods에 OPTIONS추가
- allowedOrigins에 명시 ("*"해줘도 상관 없는데, 쿠키나 JWT 토큰을 담아 보낼 경우에는 특정해줘야한다)
2. JWT
인증요청
- 원인
- 쿠키 또는 JWT토큰을 담아 보낼경우 credentials: include를 포함하여 보낸다
- 해결
- Access-Control-Allow-Credentials:true으로 설정해줘야 인증을 포함한 요청이 가능하다
- 이때 allowed credentials가 true인 경우, allowed_origin을 *로 입력할 수 없기 때문에 'http://localhost:3000' 이렇게 특정 origin을 입력해줘야한다. 참고블로그
반대로 말하면, Access-Control-Allow-Credentials이 false 인 경우 allowd_origin을 "*"으로 설정하여 모든 주소를 허용할 수 있다.
3. 중복 헤더 제거