Spring API 서버에서 PUT, DELETE 요청 시 CORS 설정이 적용 안되는 경우

안영진·2019년 10월 23일
3

Spring Backend에 Vue Frontend를 구성해 테스트를 하던 중 POST 요청은 정상적으로 동작하는데 PUT 요청 시 403 에러가 발생하는 상황을 만났습니다.

> OPTIONS http://localhost:8080/api/post/10 403

> Access to XMLHttpRequest at 'http://localhost:8080/api/post/10' from origin 'http://localhost:8081' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

에러 메시지와 요청을 살펴보니 PUT 요청 전 preflight 요청인 OPTIONS 에서 403 에러가 발생했는데 이미 Spring Security 설정에서 Frontend의 CORS와 모든 preflight 요청은 허용한 상태였습니다.

WebMvcConfigurer

@Override
public void addCorsMappings(CorsRegistry registry) {
  registry.addMapping("/**").allowedOrigins("http://frontend.domain");
}

WebSecurityConfigurerAdapter

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    ...
    .authorizeRequests()
    	.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
    ...
}

원인을 찾아보기 위해 CORS 설정을 추가한 CorsRegistry의 내용을 확인해 보았습니다.

public class CorsRegistry {
	private final List<CorsRegistration> registrations = new ArrayList<>();
    ...
	protected Map<String, CorsConfiguration> getCorsConfigurations() {
		Map<String, CorsConfiguration> configs = new LinkedHashMap<>(this.registrations.size());
		for (CorsRegistration registration : this.registrations) {
			configs.put(registration.getPathPattern(), registration.getCorsConfiguration());
		}
		return configs;
	}
}

getCorsConfigurations() 를 보니 입력받은 설정값을 CorsConfiguration 객체에 넣어주고 있습니다. 따라서 CorsConfiguration 클래스를 확인해봅니다. 위 설정에 따라 실행하면 아래 applyPermitDefaultValues() 메소드가 호출됩니다.

public class CorsConfiguration {
  	...
	private static final List<String> DEFAULT_PERMIT_METHODS = Collections.unmodifiableList(
			Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name()));
	...
	public CorsConfiguration applyPermitDefaultValues() {
		...
		if (this.allowedMethods == null) {
			this.allowedMethods = DEFAULT_PERMIT_METHODS;
			this.resolvedMethods = DEFAULT_PERMIT_METHODS
					.stream().map(HttpMethod::resolve).collect(Collectors.toList());
		}
        ...
		return this;
	}
}

applyPermitDefaultValues() 메소드를 보면 allowMethods 설정을 하지 않을 경우 GET,HEAD,POST Method를 기본값으로 추가하고 있습니다. 이때 PUT Method가 포함되지 않기 때문에 403 에러가 발생한 것입니다.

따라서 아래와 같이 허용할 설정해 주면 해결할 수 있는데 allowMethod를 추가할 경우 DEFAULT 설정이 사라지기 때문에 GET,HEAD,POST 또한 포함해 주어야 합니다.

@Override
public void addCorsMappings(CorsRegistry registry) {
  registry.addMapping("/**")
  	.allowedOrigins("http://localhost:8081")
    .allowedMethods(
    	HttpMethod.GET.name(),
    	HttpMethod.HEAD.name(),
    	HttpMethod.POST.name(),
    	HttpMethod.PUT.name(),
    	HttpMethod.DELETE.name());
}

3개의 댓글

comment-user-thumbnail
2020년 3월 19일

안녕하세요. 허용 Method 설정 관련해서 물어보고 싶은게 있습니다.
GET, POST, PUT, DELETE 을 사용하게 설정 해놨습니다.
OPTIONS 를 호출 시 403 에러가 나면서 response headers 값에
Allow : GET, POST, PUT, DELETE, TRACE, OPTIONS, PATCH 이렇게 나오는데,
보안상 문제가 될까요?

1개의 답글
comment-user-thumbnail
2021년 5월 13일

덕분에 해결했습니다!
감사합니다😊

답글 달기