이전에 했던 프로젝트에서 작성했던 코드를 보고 제대로 정리하지 않았던 것 중 CORS 가 있었다.
CORS ( Cross-Origin Resource Sharing ) 란?

서로 다른 도메인 간 통신을 할 때, 서로 다른 출처를 가지고있기 때문에 보안적인 측면에서 통신이 허용되지 않는다.
통신을 위해서는, 요청 헤더 Origin 필드에 요청 출처를 함께 담아 교차 출처 HTTP 요청을 실행해야 한다. 출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 브라우저에 구현된 스펙 기준으로 처리되며 브라우저는 클라이언트의 요청 헤더와 서버의 응답 헤더를 비교해서 최종 응답을 결정한다.
두 개의 출처를 비교하는 방법은 URL 의 구성 요소 중 Protocol, Host, Port 이 세 가지가 동일한 지만 확인한다.
Simple Request 와 PreFIight Request
1. Simple Request (본 요청)
: 예비 요청(Preflight) 없이 바로 서버에 본 요청을 한 후, 서버가 응답의 헤더에 Access-Control-Allow-Origin 과 같은 값을 전송하면 브라우저가 서로 비교 후 CORS 정책 위반여부를 검사하는 방식을 말한다.

또한, 다음과 같은 조건을 충족해야한다.
- GET, HEAD, POST
- Accept, Accept-Language, Content-Language, Content-Type 과 자동으로 설정된 헤더만 가능
- Content-type 은 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 가능
2. PreFlight Request ( 예비 요청 )
: 요청을 한 번에 보내지않고, 예비 요청과 본 요청으로 나누어 서버에 전달하는데 브라우저가 예비 요청을 보내는 것을 Preflight라고 하며 이 예비 요청의 메소드에는 OPTIONS( 리소스 변경 불가 ) 가 사용된다.
예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인한다.

CORS Configuration
- Access-Control-Allow-Origin ( *, https://developer.mozilla.org )
: 헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용
- Access-Control-Alow-Methods ( 기본값은 GET, POST, HEAD, O PTIONS, * )
: preflight request 에 대한 응답으로 실제 요청 중에 사용할 수 있는 메서드
- Access-Control-Allow-Headers ( 기본값은 Origin, Accept, X-Requested-With, Content-Type, .. )
: preflight request 에 대한 응답으로 실제 요청 중에 사용할 수 있는 헤더 필드 이름
- Access-Control-Allow-Credentials
: 실제 요청에 쿠키나 인증 등의 사용자 자격 포함가능 여부 ( Client의 credentials:include 일 경우 true )
- Access-Control-Max-Age
: preflight 요청 결과를 캐시 할 수 있는 시간으로 해당 시간동안 preflight 재요청 X
CORS with spring security
서로 다른 포트의 도메인 간 통신을 통해 테스트를 해보았다.
먼저 서버에 CORS 설정을 하지 않고 item 의 id와 그에 따른 재고를 요청하는 fetch() 함수를 작성했다. ( 강의 참고 )
- localhost:8080
<script>
function corsTest(){
fetch("http://localhost:8081/items",{
method : "GET",
headers : {
"Content-Type" : "text/xml",
}
})
.then(response => {
response.json().then(function(data){
console.log(data)
})
})
}
</script>
- localhost:8081
데이터를 응답하는 서버 Controller 는 아래와 같이 설정했다.
..
@GetMapping("/items")
public ResponseEntity<List<Item>> getItems(){
List<Item> items = itemServiceImpl.getAllItems();
return ResponseEntity.status(HttpStatus.OK).body(items);
}
}
- 실행 결과

테스트 결과, CORS error 로 출력이 되는 반면 응답 코드는 200임을 볼 수 있다.
이는 브라우저 스펙에 구현된 스펙을 기준으로 처리되기 때문이다.
- CORS 설정하기
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
..
http.cors().configurationSource(corsConfigurationSource());
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOriginPattern("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
// configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
참고
'Web' 카테고리의 다른 글
| [JPA] 지연로딩과 즉시로딩 (0) | 2023.10.08 |
|---|---|
| [JPA] 1:N 단방향 매핑과 N:1 양방향 매핑 (0) | 2023.10.01 |
| [Spring] 동시성 이슈와 synchronized 에 관하여 (0) | 2023.09.10 |
| [JPA] Dirty Checking 동작 방식 및 성능 개선 (2) | 2023.09.03 |
| [Spring] Maven Scope 와 Gradle Configurations (0) | 2023.08.27 |