관리 메뉴

ballqs 님의 블로그

[Spring] CORS란? 본문

코딩 공부/Spring

[Spring] CORS란?

ballqs 2024. 9. 14. 16:30

CORS란 무엇인가?

**CORS(Cross-Origin Resource Sharing)**는 다른 출처 간 리소스 요청을 허용하기 위한 웹 표준입니다. 기본적으로 브라우저는 보안상의 이유다른 출처에서의 리소스 접근을 차단합니다. 이를 **동일 출처 정책(Same-Origin Policy)**이라고 하는데, 동일 출처 정책에 의해 자바스크립트로 로드된 리소스가 다른 출처(서버)에 API 요청을 할 경우 브라우저가 이를 막습니다.

여기서 다른 출처란 다음 세 가지 중 하나라도 다를 경우를 의미합니다:

  1. 도메인 (예: example.com vs. api.example.com)
  2. 포트 (예: http://localhost:3000 vs. http://localhost:8080)
  3. 프로토콜 (예: http vs. https)

CORS는 이러한 제한을 완화하여 다른 출처에서의 리소스 요청을 허용하기 위해 사용됩니다.

 

CORS 작동 방식

  1. 브라우저 요청: 브라우저는 다른 출처로 리소스를 요청할 때, 그 요청이 CORS 정책에 위배되는지 확인합니다. 만약 위배된다면, 서버로 요청을 보내기 전에 preflight 요청이라고 불리는 OPTIONS 메서드 요청을 보냅니다.
  2. 서버 응답: 서버는 이 preflight 요청을 받아, 클라이언트가 보내는 실제 요청을 허용할 수 있을지 결정합니다. 이를 위해 서버는 특정 헤더(Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers)를 포함하여 응답을 돌려보냅니다.
  3. 실제 요청 처리: 만약 서버가 CORS 정책을 준수하면, 브라우저는 이후에 실제 요청을 보낼 수 있고, 그렇지 않다면 브라우저는 요청을 차단합니다.

Spring Boot에서 CORS 설정

Spring Boot에서 CORS를 설정하는 방법은 여러 가지가 있습니다. 전역 설정, 특정 컨트롤러 수준에서의 설정 등 다양한 방법을 지원하며, 아래에서 각각의 방법을 설명합니다.

 

1. 컨트롤러 레벨에서 CORS 설정

특정 컨트롤러나 메서드에 대해 CORS 설정을 하고 싶다면, @CrossOrigin 애노테이션을 사용할 수 있습니다.

@RestController
@RequestMapping("/api")
public class ExampleController {

    @CrossOrigin(origins = "http://localhost:3000")  // 특정 출처에서만 허용
    @GetMapping("/example")
    public String example() {
        return "CORS enabled!";
    }
}

 

여기서 @CrossOrigin 애노테이션은 특정 HTTP 메서드 또는 엔드포인트에서 다른 출처의 요청을 허용하는 역할을 합니다.

  • origins: 허용할 출처를 설정합니다. 예를 들어 http://localhost:3000에서 오는 요청만 허용하도록 할 수 있습니다.
  • allowedMethods: 허용할 HTTP 메서드를 설정할 수 있습니다. (예: GET, POST, PUT, DELETE)
  • maxAge: preflight 요청의 유효시간을 설정합니다. 기본값은 1800초(30분)입니다.

2. 전역(Global) CORS 설정

애플리케이션 전체에서 CORS를 적용하고 싶을 때는 WebMvcConfigurer를 구현하여 설정할 수 있습니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 모든 경로에 대해 CORS 설정
                .allowedOrigins("http://localhost:3000")  // 허용할 출처
                .allowedMethods("GET", "POST", "PUT", "DELETE")  // 허용할 HTTP 메서드
                .allowedHeaders("*")  // 모든 헤더 허용
                .allowCredentials(true)  // 쿠키 허용 여부
                .maxAge(3600);  // preflight 요청 캐싱 시간 (1시간)
    }
}

 

이렇게 하면 애플리케이션의 모든 엔드포인트에서 특정 출처의 요청을 허용하게 됩니다. addMapping("/**")을 통해 모든 경로에 대해 CORS를 적용할 수 있습니다.

 

3. Spring Security와 함께 CORS 설정

Spring Security가 적용된 애플리케이션에서는 추가적인 CORS 설정이 필요합니다. 보안 필터 체인이 먼저 동작하기 때문에 Security 설정 파일에서 CORS를 허용해야 합니다.

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.cors()  // CORS 설정 적용
            .and()
            .csrf().disable()  // CSRF 보호 비활성화 (개발 환경에서만)
            .authorizeHttpRequests()
            .anyRequest().authenticated();

        return http.build();
    }
}

여기서 http.cors()를 사용하여 Spring Security에서 CORS 설정을 적용할 수 있으며, WebMvcConfigurer와 함께 사용해야 합니다.

 

4. 프로그래밍적으로 CORS 설정 추가

특정 상황에서 Filter를 사용해 CORS 설정을 직접 커스터마이징할 수도 있습니다.

import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class SimpleCorsFilter implements Filter {

    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Max-Age", "3600");
        
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void destroy() {}
}

CORS 설정 시 주의할 점

  1. 보안:
    • Access-Control-Allow-Origin을 특정 출처로 제한하는 것이 중요합니다. *로 설정하여 모든 출처를 허용하는 것은 보안상 좋지 않습니다.
    • allowCredentials(true)로 설정하면 쿠키나 인증 정보를 포함한 요청을 허용할 수 있으므로, 신중하게 설정해야 합니다.
  2. preflight 요청:
    • OPTIONS 메서드를 사용한 preflight 요청은 서버에 추가적인 부하를 줄 수 있습니다. 필요에 따라 maxAge 값을 늘려 캐싱을 고려해보는 것이 좋습니다.
  3. API 버전별 설정:
    • 만약 여러 버전의 API를 운영한다면, 각각의 버전에 대해 별도로 CORS 정책을 설정할 수 있습니다. 예를 들어 api/v1, api/v2 등의 경로에 대해 다르게 설정할 수 있습니다.