cors 파헤치기

임종혁·2024년 4월 23일

CORS ( Cross-Origin Resource Sharing )는 브라우저가 리소스 로드를 허용해야 하는 자체 원본이 아닌 원본 (도메인, 구성표 또는 포트) 을 서버가 나타낼 수 있도록 하는 HTTP 헤더 기반 메커니즘입니다. CORS는 또한 서버가 실제 요청을 허용하는지 확인하기 위해 브라우저가 교차 출처 리소스를 호스팅하는 서버에 "실행 전" 요청을 보내는 메커니즘을 사용합니다. 해당 프리플라이트에서 브라우저는 실제 요청에 사용될 HTTP 메서드와 헤더를 나타내는 헤더를 보냅니다.

즉 cors 는 클라이언트가 다른 도메인에 있는 서버에 요청을 보낼시 출처가 다르더라도 요청과 응답을 주고 받을 수 있도록 서버에서 리소스 호출 허용된 출처를 명시해 주는 방식이다.

즉 다른 주소의 자원을 사용하기 위해서는 cors 가 필요하다.

cors 를 사용할때 preflight 를 보게 될 것이다.

이를 살펴보면 클라이언트가 서버로 부터 자원을 요청을 하는데 허락되지 않은 주소 즉 cors 가 허락하지 않은 주소라면
cors 에러를 반환할 것이다 허나 이는 서버가 클라이언트 요청을 받아들이고 모든 비지니스적 로직을 다 실행후 response를 cors error를 배출하는 것 이다.

이를 방지하기 위해 클라이언트는 요청전 preflight를 먼저 보내고 cors가 가능한지 확인을 한 후 그 후 본 요청을 보낸다.

즉 서버를 보호하기 위해 preflight를 먼저 요청한다 보면 된다.

springboot에서의 cors

우선

@RestController
public class TestController {

    @GetMapping("/")
    public ResponseEntity<?> test(){
        return ResponseEntity.ok().build();
    }
}

다음과 같은 컨트롤러 생성후

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>root</h1>

    <script>
      fetch("http://localhost:8081").then((res) => 
        console.log(res.status)
    );
    </script>
  </body>
</html>

다음과 같은 클라이언트 코드를 실행시

당연히 cors 오류가 발생한다.

우리는 이같은 오류를 WebMvcConfigurer 를 확장한 클래스를 빈을 등록하면 된다.
WebMvcConfigurer 는

@EnableWebMvc를 통해 활성화된 Spring MVC에 대한 Java 기반 구성을 사용자 정의하기 위한 콜백 메서드를 정의합니다.
@EnableWebMvc 주석이 달린 구성 클래스는 이 인터페이스를 구현하여 콜백하고 기본 구성을 사용자 정의할 수 있는 기회를 제공할 수 있습니다.
부터:

즉 spring mvf를 재 구성해주는 인터페이스다.

이 인터페이스 속
addCorsMappings 이라는 메소드가 있는 데

  @Override
    public void addCorsMappings(CorsRegistry registry) {
        

    }

해당 메소드는 다음과 같으며
registry 속에는 addMapping , allowedOrigins, allowedMethods , allowedHeaders , allowCredentials , exposedHeaders , maxAge 와 같은 속성이 존재하낟.

addMapping : 특정 경로 패턴에 cors 정책을 적용한다.
allowedOrigins: 허용된 출처를 지정한다.
allowedMethods 허용된 메서드 지정
allowedHeaders 허용된 헤더 지정
allowCredentials 브라우저 자격 증명 여부 지정
maxAge cors 요청 결과 캐시 시간 지정

만약 해당 메서드를

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    private  final long MAX_AGE_SECS = 3600;
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://127.0.0.1:5500/")
                .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
                .allowedHeaders("Access-Control-Allow-origin","*")
                .allowCredentials(true)
                .exposedHeaders("Authorization","*")
                .maxAge(MAX_AGE_SECS);

    }
}

지정시

정상적으로 동작한다는 점이다.

그렇다면 이제 궁금한것은 security 적용에서였다.

   protected void configure(HttpSecurity http) throws Exception {
        http

                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // cors 필터 걸기
                .formLogin().disable()
                .httpBasic().disable()

다음과 같은 설정을 하고 로그인시

webMvcConfig가 있는데도 불구하고 cors 오류가 나버렸다.

이는 security 설정시 기본 cors 가 block이여서 그런데
http.cors() 를 해서 cors 를 설정하면 된다.

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 내 서버가 응답을 할시 json을 자바스크립트에서 처리할 수 있게 할지 결정
        config.addAllowedOrigin("http://127.0.0.1:5000");
        config.addAllowedHeader("*"); // 모든 header 응답 허용
        config.addAllowedHeader("Access-Control-Allow-origin");
        config.addAllowedMethod("*"); // 모든 post,get,put,delete, patch요청 허용
        config.addExposedHeader("Authorization");

        source.registerCorsConfiguration("/**",config);
        return new CorsFilter(source);
    }
}

또한 corsFilter도 등록하여 cors 를 해제해주는 방법이 있는데

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 내 서버가 응답을 할시 json을 자바스크립트에서 처리할 수 있게 할지 결정
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*"); // 모든 header 응답 허용
        config.addAllowedHeader("Access-Control-Allow-origin");
        config.addAllowedMethod("*"); // 모든 post,get,put,delete, patch요청 허용
        config.addExposedHeader("Authorization");

        source.registerCorsConfiguration("/**",config);
        return new CorsFilter(source);
    }
}

다음과 같이 corsFilter 등록시

성공하는 것을 볼 수 있다.

0개의 댓글