프로젝트 중 react와 통신해야할 상황이 생겼다. cors에러를 예상하고 있었지만 막상 닥치니 해결하기까지 너무 오랜 시간이 걸렸고, 많은 우여곡절이 있었다.
CORS는 영어 그대로 교차 출처를 공유할 수 있는 권한을 부여하도록 브라우저에 알려주는 정책이다. 서로 다른 출처를 가진 Application이 서로의 Resource 에 접근할 수 있도록 해준다.

기본적으로 프로토콜, 호스트, 포트 를 통틀어서 Origin(출처) 라고 한다.
즉 서로 같은 출처란 이 셋이 동일한 출처를 말하고, 여기서 하나라도 다르다면 Cross Origin, 즉 교차출처가 되는 것이다.
http://localhost:8080 : Spring Boothttp://localhost:3000 : React보안상의 이유로, 브라우저는 스크립트에서 시작한 Cross Origin HTTP Request를 제한한다. 즉, SOP(Same Origin Policy)를 따른다.
React와 Spring Boot의 port 가 서로 다르기 때문에 cors 정책 위반 에러가 나왔던 것이다.
Request에는Origin헤더가 있고,Response에는Access-Control-Allow-Origin헤더가 있는데 이 두 가지가 서로 같을 경우 같은 출처라고 인식한다는 것을 알고 있어야 한다.

Simple Request는 Preflight Request 없이 요청을 보내 서버는 이에 대한 응답으로 Access-Control-Allow-Origin 헤더를 응답하는 방식이다.Credential이 없는 요청의 경우 "*" 와일드 카드를 통해 브라우저의 Origin에 상관없이 모든 리소스에 접근하도록 허용한다.Access-Control-Allow-Origin : Origin or *
가장 일반적으로 마주치는 시나리오이다.
먼저, OPTIONS 메서드를 통해서 다른 도메인의 리소스로 HTTP Request를 보내고 실제 요청이 전송하기에 안전한지를 확인한다. Cross-Site Request는 유저 데이터에 영향을 줄 수 있기 때문에 이와 같이 미리 전송(preflighted)한다.
Access-Control-Request-Method:<method>Access-Control-Request-Method 헤더는 실제 요청에 어떤 HTTP Method 가 사용될지 서버에게 알려주기 위해서 preflight request에 사용된다.Access-Control-Allow-Mehtods:<method> or [<method>]*Access-Control-Request-Method 헤더는 리소스에 접근할 때 허용되는 메서드를 지정한다. 이 헤더는 preflight request에 대한 응답으로 사용된다.Access-Control-Request-Headers: <header-name>[, <header-name>]*Access-Control-Request-Headers 헤더는 실제 요청에서 어떤 HTTP Header를 사용할지 서버에 알려주기 위해서 preflight request에 사용된다Access-Control-Allow-Headers 헤더는 preflight request에 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP Header를 알려준다.
HTTP Cookie 와 HTTP Authentication 정보를 사용한다.Credentials이 필요한 CORS에는 Front는 Response Header에 withCredentials=true, Backend는 Response Header에 Access-Control-Allow-Origin을 포함해야 한다.Credential Request는 Cross-site XMLHttpRequest나 Fetch 호출은 기본적으로 자격 증명을 보내지 않기 때문에 credentials 옵션을 통해 인증 보안을 강화한다. credentials 옵션이다.Access-Control-Allow-Credentials: true로 응답하지 않으면, 사용자 인증이 필요한 리소스에 대한 응답은 무시되고 웹 컨텐츠는 제공되지 않는다. Access-Control-Allow-Credentials: true | falseRequest with Credential 방식을 사용할 것인지를 지정한다.Access-Control-Allow-Credentials 헤더는 credentials 플래그가 true일 때 요청에 대한 응답을 표시할 수 있는지를 나타낸다.The HTTP Access-Control-Allow-Credentials is a Response header. The Access-Control-Allow-Credentials header is used to tell the browsers to expose the response to front-end JavaScript code when the request’s credentials mode Request.credentials is “include” | https://www.geeksforgeeks.org/http-headers-access-control-allow-credentials/
CORS 반드시 Spring Security 에 앞서 처리되어야 한다. preflight request는 JSESSIONID같은 쿠키를 포함하고 있지 않고, 이는 Request가 인증되지(not authenticated)않은 사용자라고 판단하고 거절해버리기 때문이다.
CorsFilter를 이용해서 CORS를 해결할 수 있었다. CorsFilter 는 CORS를 다룰 수 있는 가장 쉬운 방법이다. CorsConfigurationSource를 제공해 CorsFilter를 Spring Security에 통합할 수 있다.
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD","POST","GET","DELETE","PUT"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
만약 Spring MVC 의 CORS 지원을 사용한다면 Spring Security의 CorsConfigurationSource 설정을 생략할 수 있다고 한다. Spring Security는 Spring MVC에서 제공되는 CORS 설정을 활용할 수 있기 대문이다.
Spring MVC에서 CORS를 적용하는 방법은 크게 2가지로 나뉜다.
@CrossOrigin 어노테이션을 사용해서 설정하는 방법Spring MVC 설정에서 CORS를 설정하는 방법@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
@CrossOrigin 애노테이션을 통해 origins, methods, allowedHeaders, exposedHeaders, allowCredentials, maxAge 모두 설정할 수 있다.필터 기반 솔루션을 사용하는 것과 유사하지만 Spring MVC 내에서 선언하고 @CrossOrigin 설정과 결합할 수 있다.
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
.allowedOrigins("http://www.example.com")
.allowedMethods("*")
.allowCredentials(false)
.maxAge(3000);
}
}