[Spring Security] CORS 에 대하여
- CORS (Cross-Origin Resource Sharing)
CORS란 웹 어플리케이션의 도메인이 다른 도메인의 리소스에 대해서 접근이 허용되는지 체크하는 매커니즘입니다. 웹 어플리케이션은 리소스를 요청하는 서버의 도메인, 프로토콜 또는 포트가 다를 경우, cross-origin HTTP request 요청을 실행합니다.
보안상의 이유로, 브라우저는 cross-origin HTTP request에 대해서 same-origin policy를 적용하여 동작합니다. 즉, a.com 이라는 도메인의 클라이언트에서 리소스를 요청할 때는 a.com 이라는 도메인의 서버일 경우에 CORS 문제가 발생하지 않고 정상적으로 동작합니다. 만약 두 도메인이 서로 다르다면, CORS에 대해 Header 설정을 해주어야 cross-origin HTTP request 에 대해서 정상적으로 요청과 응답이 이루어집니다.
CORS 요청의 종류
CORS 요청으로는 Simple request 와 Preflight request 가 있습니다.
Simple request
Simple request 는 Preflight 체크를 하지 않으며, 클라이언트와 서버간에 한 번만 요청과 응답을 주고 받습니다. Simple request 는 아래의 조건들을 만족하면 요청하게 됩니다.
1. 요청 메서드
- GET
- HEAD
- POST
2. 커스텀 헤더를 전송을 하지 말아야 한다.
3. 허용되는 헤더는 다음과 같다.
- Accept
- Accept-Language
- Content-Language
- Content-Type
- Last-Event-ID
- DPR
- Save-Data
- Viewport-Width
- Width
4. Content-Type 헤더의 허용되는 Value 값은 다음과 같다.
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
Preflight request
Simple request 의 조건에 만족하지 않으면 Preflight request 방식으로 요청합니다. Preflight request 는 다른 도메인에 HTTP request 를 전송하기 전에 OPTIONS 메서드로 사전 요청을 통해 서버로부터 안전한 요청인지 응답을 받고, 본 요청을 수행합니다.
Spring Security 에서 CORS 문제에 대해서 간단하게 해결하는 방법입니다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
private final JwtProvider jwtProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.cors()
.and()...;
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("http://localhost:3000");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
클라이언트는 axios 를 통해서 간단하게 로그인을 위한 userId 와 password 데이터를 POST 방식으로 전송하고 있습니다.
<script>
import axios from 'axios'
export default {
name: "Login",
data() {
return {
form: {
userId: '',
password: ''
}
}
},
methods: {
tryLogin() {
axios.post('auth/login', {
userId: this.form.userId,
password: this.form.password
})
.then((res) => {
console.log(res.data);
})
.catch((e) => {
console.error(e);
})
}
}
}
</script>
요청의 Content-Type 이 application/json 이므로, Preflight request 가 발생하여 OPTIONS 메서드로 요청을 보냅니다.
Preflight request 요청으로 200 코드를 받고나서 POST로 본 요청을 수행하여 로그인 요청을 수행합니다.
참고 자료
https://dev.to/effingkay/cors-preflighted-requests--options-method-3024
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://homoefficio.github.io/2015/07/21/Cross-Origin-Resource-Sharing/#search
https://stackoverflow.com/questions/40418441/spring-security-cors-filter