예제를 살펴보자.
(이미지 출처: MDN)
https://domain-a.com
의 프론트엔드 JavaScript 코드가 XMLHttpRequest
를 사용하여 https://domain-b.com/data.json
을 요청하는 경우 보안 상의 이유로, 브라우저는 스크립트에서 시작한 교차 출처 HTTP 요청을 제한한다.
XMLHttpRequest
와 Fetch API는 동일 출처 정책을 따르기 때문에 이 API를 사용하는 웹 애플리케이션은 자신의 출처와 동일한 리소스만 불러올 수 있으며, 다른 출처의 리소스를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 한다.
Simple Request는 예비 요청(Preflight)을 과정 없이 바로 서버에 본 요청을 한 후, 서버가 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 전송하면 브라우저가 서로 비교 후 CORS 정책 위반 여부를 검사하는 방식이다.
브라우저는 요청을 한 번에 보내지 않고, 예비 요청과 본 요청으로 나누어 서버에 전달하는데, 브라우저가 예비 요청을 보내는 것을 Preflight라고 하며 이 예비 요청의 메소드에는 OPTIONS가 사용된다.
예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 것으로 요청 사양이 Simple Request에 해당하지 않을 경우 브라우저가 Preflight Request를 실행한다.
const headers = new Headers({
'Content-Type': 'text/xml',
});
fetch('https://auth-server/auth', { headers });
브라우저가 보낸 요청을 보면 Origin에 대한 정보 뿐만 아니라 예비 요청 이후에 전송할 본 요청에 대한 다른 정보들도 함께 포함되어 있다.
이 예비 요청에서 브라우저는 Access-Control-Request-Headers를 사용하여 자신이 본 요청에서 Content-Type 헤더를 사용할 것을 알려주거나, Access-Control-Request-Method를 사용하여 GET 메소드를 사용할 것을 서버에게 미리 알려주고 있다.
서버가 보내준 응답 헤더에 포함된 Access-Control-Allow-Origin: https://security.io
의 의미는 해당 URL 외의 다른 출처로 요청할 경우에는 CORS 정책을 위반했다고 판단하고 오류 메시지를 내고 응답을 버리게 된다.
URL | 동일 출처 | 근거 |
---|---|---|
https://security.io/auth?username=user1 | O | 스킴, 호스트, 포트가 동일 |
https://user:password@security.io | O | 스킴, 호스트, 포트가 동일 |
http://security.io | X | 스킴이 다름 |
https://api.security.io | X | 호스트가 다름 |
https://security.io:8000 | ? | 브라우저의 구현에 따라 다름, explorer는 포트 자체를 무시함 |
CorsFilter
를 추가한다.corsFilter
라는 이름의 Bean이 제공되면 해당 필터가 사용된다.corsFilter
라는 이름의 Bean이 없고 CorsConfigurationSource
Bean이 정의된 경우 해당 Bean이 사용된다.CorsConfigurationSource
Bean이 정의되어 있지 않은 경우 Spring MVC가 클래스 경로에 있으면 HandlerMappingIntrospector
가 사용된다.CorsConfigurationSource
를 통해 일치된 정책에 따라 CORS 응답 헤더와 같은 응답을 업데이트하기 위한 필터이다.@CorsOrigin
javax.servlet
에서 CORS 검사를 수행해야 하는 보안 제얀 조건에 유용한 필터이다.