Spring/Spring Security

Spring MVC와 Security에서의 CORS 설정

윤밥밥 2024. 8. 16. 17:04

이 포스트를 통해

  • Spring MVC에서의 CORS 설정
  • Spring Security를 사용했을 때의 CORS 설정

을 적어보려 한다.

 

만약 cors 자체에 대해 이해가 부족한 상황이라면 아래 포스트 글을 참조하면 좋을 거 같다.

정리가 매우 잘 되어 있어 첨부했다.

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F

Spring MVC와 Spring Security를 구분한 이유

Spring에서 CORS 이슈가 발생할 수 있는 곳은 MVC단과 Security단이 있다.

 

이렇게 구분하는 이유는 각 요청이 처리하는 곳이 다르기 때문이다.

인증, 인가가 필요한 요청을 Security단이 요청을 받아 응답을 반환하고,

인증, 인가가 필요없는 요청에 대해서는 Security단을 지나치고 MVC단에서 요청을 받아 응답을 한다.

 

이 상황에서 CORS 에러가 터지는 이유는 Preflight 요청 떄문이다.

브라우저는 실제 요청을 보내기 전에 Preflight 요청을 먼저 보내게 된다.

요청을 받은 서버는 이 요청 url을 확인해 Security단 혹은 MVC단에서 처리한다.

 

여기서 MVC단에 대한 CORS 설정만 해주고, Security단 cors를 설정 안해주면 에러가 터질 수 있다.
Prefilight요청은 Security에서 처리하는 데, CORS는 MVC단에만 설정되어 있어 응답에 CORS 설정이 되지 않기 때문이다.

 

과정에 따라 상황을 정리해보자면

  1. 브라우저는 실제 요청을 보내기 전에 Prefilight 요청을 보내 서버를 통해 이 요청을 보내도 되는 지 판단한다.(login id, pw등의 민감정보를 보내기 전에 검사하는 거라 생각하면 된다.)
  2. Preflight 요청을 받은 서버는 이에 대한 응답을 한다.
    1. Security를 거쳐야하는 요청이면 Security단이 응답을 한다.
    2. Security를 거치지 않는 요청이면 MVC단이 응답을 한다.
  3. 응답 헤더에는 Access-Control-Allow-Origin 필드에는 허용하는 url이 포함되어 있다.
  4. 브라우저는 이 Access-Control-Allow-Origin 필드를 보고 자신의 웹페이지의 출처가 포함되어 있는 지 확인한다.
  5. 포함되어 있으면 실제 요청을 해 응답을 받아온다.(물론 이 응답에도 Access-Control-Allow-Origin가 포함되어 있다.)
  6. 포함되어 있지 않다면 CORS 에러를 반환한다.

1. Spring MVC에서의 CORS설정

만약 Spring Security를 사용하지 않는다면 이 CORS 설정만으로도 충분하다.

Spring MVC에서 CORS를 설정하는 방법은 두 가지가 있다.

  1. 각 Controller Method에 설정
  2. 전역으로 설정

각 Controller Method에 설정하는 방법

@CrossOrigin 애노테이션을 활용하는 방법이다.

@CrossOrigin 애노테이션을 사용하여 특정 컨트롤러나 메서드에 대해 CORS 정책을 설정할 수 있다.

컨트롤러에 설정

@RestController
@CrossOrigin(origins = "http://localhost:3000") //이 출처에서 오는 요청 허용
@RequestMapping("/api")
public class MyController {

    @GetMapping("/data")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("This is data");
    }
}

특정 메서드에만 설정

@RequestMapping("/api")
public class MyController {

    @GetMapping("/data")
    @CrossOrigin(origins = "http://localhost:3000") //getData에만 cors 설정
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("This is data");
    }

    @PostMapping("/submit")
    public ResponseEntity<String> submitData() {
        return ResponseEntity.ok("Data submitted");
    }
}

전역으로 설정하는 방법

  1. WebConfig라는 클래스를 만든다.
  2. 아래와 같이 작성한다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCors(final CorsRegistry registry) {
        registry
            .addMapping("/**")
          .allowedHeaders("*")
          .allowedOrigins("http://localhost:3000");
          .allowedMethods("*");

    }
}

이 설정은 MVC단에 대한 CORS를 설정해주는 설정 클래스이다.

이 설정파일을 읽어 자동으로 응답에 CORS 관련 헤더들을 추가해준다.

1. registry.addMapping("/")

  • 역할: 모든 경로(/**)에 대해 CORS 정책을 적용한다.
  • 의미: 이 설정은 애플리케이션의 모든 엔드포인트에 대해 CORS 요청을 허용한다는 의미이다.

2. allowedHeaders("*")

  • 역할: 모든 요청 헤더를 허용한다.
  • 의미: 클라이언트가 어떤 헤더를 요청에 포함하든, 서버는 이를 허용하겠다는 의미입니다. 예를 들어, Content-Type, Authorization 등의 모든 헤더가 허용된다.
  • allowedHeaders("SET-COOKIE", "Authorization") 등으로 특정 헤더만 허용할 수 있다.

3. allowedOrigins("http://localhost:3000")

  • 역할: http://localhost:3000에서 오는 요청만을 허용한다.
  • 의미: 이 설정은 http://localhost:3000 출처에서 오는 요청만이 CORS 정책에 따라 허용된다는 것을 의미한다.
  • 이 부분에 프론트의 url(http, 도메인, 포트)을 명시한다.

4. allowedMethods("*")

  • 역할: 모든 HTTP 메서드를 허용한다.
  • 의미: 이 설정은 GET, POST, PUT, DELETE, OPTIONS, PATCH 등 모든 HTTP 메서드에 대한 요청을 허용한다는 것을 의미한다.
  • allowedMethods("GET", "POST") 등으로 특정 Method만 허용할 수 있다.

Spring Security를 사용했을 때의 CORS 설정하는 방법

시큐리티 단에서 CORS 설정은 MVC단과 다르다.

시큐리티단을 거치는 모든 api에 대해서는 시큐리티 단에서 CORS 설정을 해줄 수 있다.

(Security설정을 하면 모든 요청에 대해 우선적으로 시큐리티단에서 요청을 받기에 별도로 MVC단에 대한 CORS 설정을 해줄 필요가 없다.)

 

설정하는 방법은 다음과 같다.

  1. Cors관련 파일(CorsConfig)을 생성한다.
  2. SecurityConfig에 적용해준다.

CorsConfig

@Configuration
public class CorsConfig {

  @Bean
  public UrlBasedCorsConfigurationSource corsConfigurationSource() {
      CorsConfiguration configuration = new CorsConfiguration();

      // Using set methods instead of add methods
      configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
      configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
      configuration.setAllowedHeaders(Arrays.asList("*"));
      configuration.setAllowCredentials(true);

      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      source.registerCorsConfiguration("/**", configuration);
      return source;
  }
}

1. corsConfigurationSource 메서드

  • 메서드 역할: 이 메서드는 CORS 정책을 설정하는 UrlBasedCorsConfigurationSource 객체를 생성하고 반환한다. 이 객체는 URL 경로에 따라 CORS 정책을 등록하고 관리하는 역할을 한다.

2. CorsConfiguration 객체

  • 역할: CORS 정책을 정의하는 객체이다. 이 객체에서 CORS 정책의 세부 사항을 설정한다.
  • setAllowedOrigins(Arrays.asList("http://localhost:3000")):
    • http://localhost:3000 출처(origin)에서 오는 요청만 허용하도록 설정한다.
    • CORS 정책에서 허용할 출처를 설정하는 부분이다.
  • setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")):
    • 허용할 HTTP 메서드를 설정이다. 여기서는 GET, POST, PUT, DELETE, OPTIONS 메서드를 허용하도록 설정이다.
  • setAllowedHeaders(Arrays.asList("*")):
    • 모든 헤더를 허용한다. 클라이언트가 어떤 헤더를 사용해 요청하든지 서버에서 이를 허용하도록 설정한다.
  • setAllowCredentials(true):
    • 자격 증명(예: 쿠키, 인증 헤더 등)을 포함한 요청을 허용하도록 설정한다. 이 설정을 true로 하면 클라이언트에서 자격 증명을 포함해 서버에 요청을 보낼 수 있다.

3. UrlBasedCorsConfigurationSource 객체

  • 역할: URL 패턴에 따라 CORS 정책을 등록하고 관리하는 역할을 한다.
  • `source.registerCorsConfiguration("/", configuration)`**:
    • 모든 경로(/**)에 대해 위에서 설정한 CORS 정책(configuration)을 적용한다.
    • 즉, 애플리케이션의 모든 엔드포인트에 대해 위에서 설정한 CORS 정책이 적용한다.`

SecurityConfig 설정

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

        private final UrlBasedCorsConfigurationSource ConfigurationSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(ConfigurationSource)) // 이 부분
            .csrf().disable()
            .authorizeRequests()
                .anyRequest().authenticated();
    }
}

참고: SecurityConfig(시큐리티 설정파일)에서 .cors(AbstractHttpConfigurer::disable) 은 무슨 설정이었을까?

이 설정을 말 그대로 Security단에서 Cors 설정을 하지 않는다는 것이다.

즉 cors 인증을 해야하는 요청에 대해서도 Security단에서 처리해주지 않는다는 의미이다.

즉 동일 출처에 대해서만 허용한다 생각하면 될 거 같다.

결론

Security를 사용하는 상황과 안 사용하는 상황을 잘 구분하여 설정를 해주자.