[EasyValet.Trouble Shooting] #4. Cross Origin Resource Sharing(CORS)
개요.
산초의 브라우저에서 api 서버로 회원가입 요청을 보내는 작업에서 CORS 설정하라는 에러가 발생했습니다. 어떻게 발생하게 되는 것이고 해결은 어떻게 하는지 기록으로 남기려합니다.
기록을 남기기전 기록을 위한 정보는 MDN에서 가져왔습니다.
CORS란?
다른 출처에서 온 데이터가 신뢰할 수 있는지 확인하는 정책이다.
브라우저에는 두개의 데이터에 대해 두가지 정책이 존재합니다.
SOP, CORS.
sop는 same origin policy 이고 CORS는 Cross origin resource sharing 입니다. 공통점과 차이점을 보면 어떤 의미인지 알 수 있습니다. Same origin vs Cross origin 이 두가지는 같은 출처 와 교차 출처라고 해석됩니다. 전자는 같은 출처에서만 소스를 공유할 수 있다는 의미입니다. 후자는 교차 출처 소스 공유 정책으로 브라우저에서 다른 서버로 리소스를 요청할 때 지켜야하는 규약입니다. 다른 API서버에서 반환되는 response를 특정 약속과 함께 반환해야한다는 약속과도 같습니다.
Origin.
출처를 의미하며 이 출처를 비교하는 기준은 아래와 같습니다.
https://127.0.0.1:8080
밑줄처럼 세개의 영역으로 나눠지고 각각 protocol,host,port로 나눠지게됩니다. 이 세개가 모두 동일하면 same origin으로 식별합니다. 동일 출처에서 온 소스라고 인식하는 것입니다. 하지만 이중에 하나라도 달라지게 되면 CORS정책을 위반하게 됨으로 특별한 조치가 필요합니다.
현재 프로젝트에서는 스프링 부트를 사용하고 있기 때문에 스프링 부트에서 해결한 방법을 공유하고자합니다.
CORS 정책에서 특정 호스트 허가하기.
1. 클라이언트(브라우저)에서 HTTP request에 Origin 헤더를 추가한다. value값으로 요청을 보낼 Origin(protocol + host + port)를 작성하여 다른 서버로 요청을 전송해야합니다.
2. 서버에서는 Http 응답헤더에 Allow-Controll-Allow-Origin 헤더를 추가합니다. value값으로 응답을 반환할 때 클라이언트의 Origin을 작성하여 응답을 반환해야합니다.
3. 클라인트가 이를 인증하게되는데 이 로직은 브라우저에 구현되어 있습니다. 이 말은 타 서버는 이 인증에 대해 알 필요가 없고 약속에 맞는 헤더만 추가해서 반환해주면 됩니다. 브라우저는 이 일치여부를 체크하고 일치하면 해당 응답을 받고 그렇지 않은 경우는 응답을 버린뒤 CORS 정책을 위반했음을 명시해줍니다.
문제 정의.
요청하는 데이터의 출처가 기존과는 다르기 때문에 발생합니다. CORS 정책을 위반한 상황입니다. 다른 출처(서버)에서 리소스를 보낼 때 CORS 정책에서 제공하는 약속을 지키지 않았기 때문에 발생했습니다.
해결.
spring에는 몇가지 방법의 CORS 정책위반을 해결하는 단순하고도 강력한 기능들을 제공합니다. 반환을 제공하는 controller의 엔드포인트 메서들에 @CrossOrigin을 추가하고 Allow-Controll-Allow-Origin을 태그로 추가하는 방법이 있습니다. 이렇게 될 경우 모든 컨트롤러 클래스명 위에 어노테이션을 붙이거나 각 요청에 어노테이션을 작성해야합니다. 이러한 방법은 코드의 중복을 발생시킨다고 판단했고 global하게 적용 시키기로 했다.
@Configuration
@EnableWebMvc
public class CORSConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
// .allowedOrigins("*") // deprecate됨 allowCredentials(true)과 함께 못씀
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("*")
.allowCredentials(true).maxAge(3600);
// Add more mappings...
}
}
현재 도메인도 없고 중요 정보도 없기때문에 모든 경우 요청 Origin에 따라 모두 CORS 정책을 지키게끔 설정해놨습니다. 나중에 web Server가 구성되면 변경하거나 프론트엔드에서 해결할 경우 사라질 수도 있는 코드 입니다.