CORS(Cross-Origin Resource Sharing)에 대한 소개입니다.
1. CORS 개요
크로스 도메인 리소스 공유는 보안상의 이유로 브라우저가 현재 도메인 외부의 자원에 대한 AJAX 요청을 허용하지 않는 것을 해결하기 위한 방법입니다. CORS는 클라이언트와 서버 모두에서 지원해야 하며, 모든 주요 브라우저들이 이 기능을 지원하고 있습니다. IE에서는 최소한 버전 10 이상이 필요합니다. 클라이언트 측면에서는 CORS 통신 과정이 자동으로 처리되므로 사용자 개입이 필요하지 않습니다. 개발자 입장에서는 동일한 출처의 AJAX 통신과 차이가 없습니다. 브라우저는 AJAX 요청이 크로스 도메인인 경우 자동으로 몇 가지 추가적인 헤더 정보를 붙이고, 때때로 추가적인 요청을 생성할 수 있지만 사용자는 이를 인지하지 못합니다. 따라서 CORS 통신의 핵심은 서버 측 구현에 있습니다.
2. 크로스 도메인 요청 분류
크로스 도메인 요청은 간단한 요청과 복잡한 요청으로 나뉩니다. 다음 조건을 모두 만족하면 간단한 요청으로 분류됩니다:- 요청 메소드: GET, POST, HEAD 중 하나
- 요청 헤더: Accept, Accept-Language, Content-Language, Content-Type(단, application/x-www-form-urlencoded, multipart/form-data, text/plain만 가능)
3. 크로스 도메인 요청 처리 방법
3.1 간단한 요청
간단한 요청의 경우, 브라우저는 자동으로 `Origin` 헤더를 추가하여 요청의 출처를 알려줍니다. 서버는 이 값을 확인하고, 허용 목록에 없으면 `Access-Control-Allow-Origin` 헤더를 포함하지 않은 응답을 반환하며, 브라우저는 이에 대해 예외를 발생시킵니다. 허용된 경우, 다음과 같은 헤더를 반환합니다:- `Access-Control-Allow-Origin`: 요청 헤더의 Origin 값
- `Access-Control-Allow-Credentials`: 쿠키 전송 여부 (선택적)
- `Access-Control-Expose-Headers`: 응답 헤더에서 접근 가능한 필드 (기본값: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma)
3.2 복잡한 요청
복잡한 요청의 경우, 먼저 '프리플라이트(preflight)' 요청을 보내 서버가 요청을 허용하는지 확인합니다. 프리플라이트 요청은 캐시될 수 있으며, 유효기간 내에 다시 요청 시 프리플라이트 요청을 생략할 수 있습니다.3.2.1 프리플라이트 요청
- 요청 메소드: OPTIONS
- 요청 헤더: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
- 서버는 위 세 가지 필드를 확인하여 요청을 허용할지 결정합니다.
- 응답 헤더:
- `Access-Control-Allow-Origin`: 요청 헤더의 Origin 값
- `Access-Control-Allow-Credentials`: 쿠키 전송 여부 (선택적)
- `Access-Control-Allow-Headers`: 허용되는 추가 헤더 필드 (선택적)
- `Access-Control-Allow-Methods`: 허용되는 HTTP 메소드
- `Access-Control-Max-Age`: 프리플라이트 요청의 캐시 시간
4. Spring에서 CORS 설정
4.1 @CrossOrigin 어노테이션
이 어노테이션은 클래스나 메소드에 적용할 수 있습니다. 속성은 다음과 같습니다:- origins: 허용된 도메인 ('*'는 모든 도메인을 허용)
- allowedHeaders: 허용된 요청 헤더
- exposedHeaders: 응답 헤더에서 접근 가능한 필드
- methods: 허용된 HTTP 메소드
- allowedCredentials: 쿠키 전송 여부 (기본값 false)
- maxAge: 프리플라이트 요청의 캐시 시간
4.2 전역 설정
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST")
.allowedHeaders("header1", "header2")
.exposedHeaders("header1")
.allowCredentials(true)
.maxAge(7200);
}
}
4.3 CorsFilter
Spring Security와 함께 사용할 때 CorsFilter를 통해 CORS 지원을 적용할 수 있습니다.
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:8080");
config.addAllowedHeader("custom-header");
config.addAllowedMethod("*");
config.setMaxAge(60L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}