교차 출처 리소스 공유(Cross-origin resource sharing)로,
외부(다른 도메인 서버)에서 온 자원소스들이 접근하는(=크로스 도메인) 체계를 말한다.
=> URL의 요소중 Protocol, Host, Port를 합친 것
페이지를 만들다보면 다른 출처의 자원(css, html, js 등)이 필요한 경우가 생긴다. 하지만 브라우저에서는 SOP로 인해 이미 다른 출처의 자원과 상호작용이 제한되어있다.
동일한 출처에 대한 정책을 의미한다.
동일한 출처에만 리소스를 공유할 수 있다.`라는 법률을 가진고 있다.
Cors와 반대 의미로 생각하면 쉽다.
이 때, CORS는 HTTP 헤더를 사용해 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 부여해준다.
브라우저에서 입력한 정보가 다른 서버로 전송될 수 있기 때문!
이미 XMLHTTPRequest
객체의 보안상 제한되어 있다.
기본적으로 보안상의 이유로 브라우저는 Same-origin policy(SOP, 동일 출처 정책)
을 따른다. 그렇기 때문에 XMLHttpRequest
와 fetch API
는 동일 출처 정책에 따라 동일한 출처의 리소스만을 요청할 수 있다.
하지만 웹 애플리케이션을 구현하다보면, 다른 출처에 있는 리소스를 요청하는 경우가 많습니다. 일례로, SPA(Single Page Application)
로 구현된 웹 애플리케이션에서 다른 도메인을 가진 API 서버에 요청하는 경우는 매우 비일비재하다. 또한, web font을 요청하는 경우도 마찬가지다.
cross-origin 요청을 필요한 경우가 많아지고 있기 때문에, 이를 안전하게 처리할 정책이 필요했다. 그래서 CORS가 나온 것다. CORS는 cross-origin 요청을 제한적으로 허용함
으로써 좀 더 안전하게 cross-origin 요청을 처리할 수 있는 정책인 것이다.
인증정보를 포함한 요청 (credentialed requests)
자바스크립트로 크로스 오리진 요청을 보내는 경우, 기본적으로 쿠키나 HTTP 인증 같은 자격 증명(credential)이 함께 전송되지 않는다.
서버에서 이를 허용하고 싶으면, 자격증명이 달린 header
를 명시적으로 허용하겠다는 세팅이 서버에 필요하다.
자격증명을 함께 전송하는 방법
자격 증명 정보가 담긴 요청을 서버에서 받아들이기로 동의했다면 서버는 응답에 Access-Control-Allow-Origin 헤더와 함께 Access-Control-Allow-Credentials: true 헤더를 추가해서 보낸다.
자격 증명이 함께 전송되는 요청을 보낼 땐 Access-Control-Allow-Origin에 *을 쓸 수 없습니다.
nginx 서버 배포해서 사용
웹팩 devServer 사용시 webpack proxy 사용
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081'
}
}
}
}
// react
// package.json
"proxy": "http://localhost:8080"
1) CrossOrigin 어노테이션
/*
@CrossOrigin(origins = “허용주소:포트”,maxAge=3600)
모든 origin 허용은 @CrossOrigin(origins="*") 설정
maxAge로 preflight 캐시 저장기간 설정
*/
// http://localhost:8080 에서 들어오는 요청만 CORS 허용
@CrossOrigin(originPatterns = "http://localhost:8080")
@RestController
public class LoginController {
@GetMapping("/login")
public String login() {
return "로그인 성공! ID: jayon, PW: 1234";
}
}
2) SpringBoot Security @CrossOrigin 설정
SecurityConfig
.addFilter(corsFilter)
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
configuration.addExposedHeader("Content-Disposition");
configuration.setMaxAge(3000L); // preflight
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
3) WebConfig 설정(✨)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedHeaders("*") // 모든 헤더를 허용
.allowedMethods(
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name()
);
}
}
//
or
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOriginPatterns("*") // 모든 요청을 허용
.allowedHeaders("*") // 모든 헤더를 허용
.allowedMethods("*"); // 모든 메소드를 허용
}
}
preflight란?
- 브라우저는 cross-origin 요청을 전송하기 전에 OPTIONS 메소드로 사용해 요청을 보낸다.
- Response로 Access-Control-Allow-Origin과 Access-Control-Allow-Methods가 넘어오는데 이는 서버에서 어떤 origin과 어떤 method를 허용하는지 브라우저에게 알려주는 역할을 한다.
- 브라우저는 OPTIONS 요청이 반환되면 CORS 여부 및 요청메소드 사용 가능 여부를 확인하고, cross-origin(원래 보내려던 원요청) 요청을 보낸다.
-> 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 체크하는 것이 preflight
-> 매번 preflight를 보내지는 않고 캐시 저장 기간을 설정하여 보관할 수 있다.