Cors( 교차 출처 리소스 공유 )는 추가 HTTP 헤더를 사용하여 , 한 출처에서 실행 중인 웹 애플리케이션이
다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제 입니다.
다른 출처란 - Origin 이 다른 리소스 입니다.
http://locaohost.com:3000/api/login
위와 같은 URL의 경우 포트번호 까지 (http://localhost.com:3000)를 Origin 이라고 합니다.
Origin은 프로토콜(http), 도메인(localhost.com) , 포트번호(3000) 까지를 포함하며 CORS 란 서로 다른 Origin에서 리소스에 접근할때 발생하는 오류입니다.
https://www.do-1.com:3000 (클라이언트) - > https://www.do-1.com:8080 (서버)
ex) 포트가 다른경우 ( 3000 -> 8080 )
https://www.do-1.com (클라이언트) - > https://www.www.do-2.com (서버)
ex) 도메인이 다른경우 ( www.do-1.com - > www.do-2.com )
http://www.do-1.com (클라이언트) - > https://www.do-1.com (서버)
ex) 프로토콜이 다른경우 (http: - > https)
위의 3가지 예제는 보안상이유로 CORS HTTP 요청을 제한합니다.
예를 들어,
XMLHttpRequest(서버와 통신하는 axios or ajax 요청)은 동일 출처 정책을 따릅니다.
즉, API를 사용하는 웹 애플리케이션은 자신의 출처(Client)와 동일한 리소스만 불러올 수 있으며,
다른 출처(Server)의 리소스를 불러오려면 올바른 CORS 헤더를 포함한 응답을 반환받아야 합니다.
Access to XMLHttpRequest at 'https://www.do-1.com:3000' from origin 'https://www.do-1.com:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
서로 다른 Origin 이기 때문에 CORS 보안이 적용되면서,
CORS 정책위반 에러가 발생하였고, 이유는 Access-Control-Allow-Origin이라는 헤더가 응답헤더에 없다는 뜻 입니다.
Origin 이 서로 다른 곳으로 리소스를 요청 할 때에는
requestHeader 에 자동으로 Origin:'https://www.do-1.com:3000'
즉 본인의 출처를 기재하여 서버로 전송됩니다 .
그렇지만 서버에서는 'https://www.do-1.com:3000' 라는 외부 출처에게 리소스를 허락해 준다는 설정을 하지 않았기 때문에 발생하는 에러 입니다 .
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://www.do-1.com:3000")//해당 요청 Origins 대하여 응답 허용.
.allowedMethods("PUT", "DELETE", "POST", "GET")//해당 요청 Methods 응답 허용.
.allowedHeaders("header1", "header2", "header3")//해당 요청 헤더에 대하여 응답 허용
.allowCredentials(true).maxAge(3600);
}
}
위와 같이 allowedOrigins("http://www.do-1.com:3000") 부분을 스프링 부트 전역설정에 해준다면 해당 Origin의 모든 요청에 대하여 응답을 허용해 주므로 에러를 해결할 수 있습니다 .
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
위와 같이 설정 후 configure 설정에 등록후 필터로 사용 합니다.
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
이 방식은 스프링부터 4.2 버전 이상부터 사용 가능하며 컨트롤러, 메서드 별로 설정이 가능합니다.
allowedOrigins : default = '*' -> 접근하는 모든 Origin 에게 리소스를 허용.( 보안 취약 )
allowedMethods : default = "GET","HEAD","POST"-> DELETE, PUT 등의 메서드 요청이 올 경우 에러 발생 (수동으로 추가 해줘야함 )
allowedHeaders : defalt = "*"
allowCredentials : 자격 증명 모드(Request.credentials -> "include")로 요청을 받거나,
cookies or Authentication 정보를 인식해야할 경우 allowCredentials("true") 사용
preflight request에 대한 응답의 일부로 사용하는 경우,
credentials 사용하여 실제 요청을 수행할 수 있는지를 나타낸다.
maxAge : preflight request 요청 결과를 캐시할 수 있는 시간 (preflight 아래 설명 참조)
일부 요청은 CORS preflight 없이 1번의 요청만 전송합니다 .
1.사용 메서드는 GET,HEAD,POST일 경우
2. Accept / Accept-Language / Content-Language / Content-Type
DPR / Downlink / Save-Date / Viewport-Width / Width 외의 수동으로 설정한 헤더값이 없어야함.
3.Content-Type 헤더의 값은
application/x-www-form-urlencoded, multipart/form-data, text/plain
심플 리퀘스트와 달리 먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로(ex.API SERVER) HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인합니다 .
Cross-Site 요청은 유저 데이터에 영향을 줄 수 있기 때문에 미리 전송하는 요청을 뜻합니다 .
Preflighted request로 요청을 보내는 경우는 Simple requests 의 기본헤더 외의 헤더값을 추가하여 전송할 경우 발생합니다 .
Cookies 나 Authentication 정보를 인식가능한 요청.
크리덴셜 리퀘스트를 사용하고, Cookie 를 요청헤더에 포함한 경우
Access-Control-Allow-Origin 헤더의 값이 * 인 경우 요청이 실패합니다.
"http://do-1.com/" 처럼 명시를 해줘야 합니다 .