๐[์คํ๋ง ์ํ๋ฆฌํฐ OAuth2] CORS ์ดํด
๊ฐ๋ ์ค๋ช ๋ฐ API
CORS(Cross-Origin Resource Sharing, ๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ )
- HTTP ํค๋๋ฅผ ์ฌ์ฉํ์ฌ, ํ ์ถ์ฒ์์ ์คํ ์ค์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ค๋ฅธ ์ถ์ฒ์ ์ ํํ ์์์ ์ ๊ทผํ ์ ์๋ ๊ถํ์ ๋ถ์ฌํ๋๋ก ๋ธ๋ผ์ฐ์ ์ ์๋ ค์ฃผ๋ ์ฒด์
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฆฌ์์ค๊ฐ ์์ ์ ์ถ์ฒ์ ๋ค๋ฅผ ๋ ๋ธ๋ผ์ฐ์ ๋ ์์ฒญ ํค๋์ Origin ํ๋์ ์์ฒญ ์ถ์ฒ๋ฅผ ํจ๊ป ๋ด์ ๊ต์ฐจ ์ถ์ฒ HTTP ์์ฒญ์ ์คํํ๋ค.
- ์ถ์ฒ๋ฅผ ๋น๊ตํ๋ ๋ก์ง์ ์๋ฒ์ ๊ตฌํ๋ ์คํ์ด ์๋ ๋ธ๋ผ์ฐ์ ์ ๊ตฌํ๋ ์คํ ๊ธฐ์ค์ผ๋ก ์ฒ๋ฆฌ๋๋ฉฐ ๋ธ๋ผ์ฐ์ ๋ ํด๋ผ์ด์ธํธ์ ์์ฒญ ํค๋์ ์๋ฒ์ ์๋ตํค๋๋ฅผ ๋น๊ตํด์ ์ต์ข ์๋ต์ ๊ฒฐ์ ํ๋ค.
- ๋๊ฐ์ ์ถ์ฒ๋ฅผ ๋น๊ตํ๋ ๋ฐฉ๋ฒ์ URL์ ๊ตฌ์ฑ์์ ์ค Protocol, Host, Port ์ด ์ธ๊ฐ์ง๊ฐ ๋์ผํ์ง ํ์ธํ๋ฉด ๋๊ณ ๋๋จธ์ง๋ ํ๋ ค๋ ์๊ด์๋ค.
- https://domain-a.com ์ ํ๋ก ํธ ์๋ JavaScript ์ฝ๋๊ฐ XMLHttpRequest๋ฅผ ์ฌ์ฉํ์ฌ https://domain-b.com/data.json์ ์์ฒญํ๋ ๊ฒฝ์ฐ ๋ณด์ ์์ ์ด์ ๋ก, ๋ธ๋ผ์ฐ์ ๋ ์คํฌ๋ฆฝํธ์์ ์์ํ ๊ต์ฐจ ์ถ์ฒ HTTP ์์ฒญ์ ์ ํํ๋ค.
- XMLHttpRequest์ Fetch API๋ ๋์ผ ์ถ์ฒ ์ ์ฑ ์ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์ ์ด API๋ฅผ ์ฌ์ฉํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ์ ์ถ์ฒ์ ๋์ผํ ๋ฆฌ์์ค๋ง ๋ถ๋ฌ์ฌ ์ ์์ผ๋ฉฐ, ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ๋ถ๋ฌ์ค๋ ค๋ฉด ๊ทธ ์ถ์ฒ์์ ์ฌ๋ฐ๋ฅธ CORS ํค๋๋ฅผ ํฌํจํ ์๋ต์ ๋ฐํํด์ผํ๋ค.
๋์ผ ์ถ์ฒ ๊ธฐ์ค
CORS ํด๊ฒฐ - ์๋ฒ์์ Access-Control-Allow-* ์ธํ
Access-Control-Allow-Origin
- ํค๋์ ์์ฑ๋ ์ถ์ฒ๋ง ๋ธ๋ผ์ฐ์ ๊ฐ ๋ฆฌ์์ค๋ฅผ ์ ๊ทผํ ์ ์๋๋ก ํ์ฉํ๋ค- *, https://security.io
Access-Control-Allow-Methods
- preflight request ์ ๋ํ ์๋ต์ผ๋ก ์ค์ ์์ฒญ ์ค์ ์ฌ์ฉํ ์ ์๋ ๋ฉ์๋๋ฅผ ๋ํ๋ธ๋ค- ๊ธฐ๋ณธ๊ฐ์ GET,POST,HEAD,OPTIONS, *
Access-Control-Allow-Headers
- preflight request ์ ๋ํ ์๋ต์ผ๋ก ์ค์ ์์ฒญ ์ค์ ์ฌ์ฉํ ์ ์๋ ํค๋ ํ๋ ์ด๋ฆ์ ๋ํ๋ธ๋ค- ๊ธฐ๋ณธ๊ฐ์ Origin,Accept,X-Requested-With,Content-Type, Access-Control-Request-Method,Access-Control-Request-Headers, Custom Header, *
Access-Control-Allow-Credentials
- ์ค์ ์์ฒญ์ ์ฟ ํค๋ ์ธ์ฆ ๋ฑ์ ์ฌ์ฉ์ ์๊ฒฉ ์ฆ๋ช ์ด ํฌํจ๋ ์ ์์์ ๋ํ๋ธ๋ค. Client์ credentials:include ์ผ๊ฒฝ์ฐ true ํ์Access-Control-Max-Age
- preflight ์์ฒญ ๊ฒฐ๊ณผ๋ฅผ ์บ์ ํ ์ ์๋ ์๊ฐ์ ๋ํ๋ด๋ ๊ฒ์ผ๋ก ํด๋น ์๊ฐ๋์์ preflight์์ฒญ์ ๋ค์ ํ์ง ์๊ฒ ๋๋ค
CorsConfigurer
- Spring Security ํํฐ ์ฒด์ธ์ CorsFilter๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- corsFilter ๋ผ๋ ์ด๋ฆ์ Bean์ด ์ ๊ณต๋๋ฉด ํด๋น CorsFilter๊ฐ ์ฌ์ฉ๋๋ค
- corsFilter ๋ผ๋ ์ด๋ฆ์ Bean์ด ์๊ณ CorsConfigurationSource ๋น์ด ์ ์๋ ๊ฒฝ์ฐ ํด๋น CorsConfiguration์ด ์ฌ์ฉ๋๋ค.
- CorsConfigurationSource ๋น์ด ์ ์๋์ด ์์ง ์์ ๊ฒฝ์ฐ Spring MVC๊ฐ ํด๋์ค ๊ฒฝ๋ก์ ์์ผ๋ฉด HandlerMappingIntrospector๊ฐ ์ฌ์ฉ๋๋ค.
CorsFilter
- CORS ์๋น ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ CORS ๋จ์ ๋ฐ ๋ณธ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , ์ ๊ณต๋ CorsConfigurationSource ๋ฅผ ํตํด ์ผ์น๋ ์ ์ฑ ์ ๋ฐ๋ผ CORS ์๋ต ํค๋์ ๊ฐ์ ์๋ต์ ์ ๋ฐ์ดํธํ๊ธฐ ์ํ ํํฐ์ด๋ค
- Spring MVC Java ๊ตฌ์ฑ๊ณผ Spring MVC XML ๋ค์์คํ์ด์ค์์ CORS๋ฅผ ๊ตฌ์ฑํ๋ ๋์์ด๋ผ ๋ณผ ์ ์๋ค ( ์: @CorsOrigin)
- ์คํ๋ง ์น์ ์์กดํ๋ ์์ฉ ํ๋ก๊ทธ๋จ์ด๋ javax.servlet ์์ CORS ๊ฒ์ฌ๋ฅผ ์ํํด์ผ ํ๋ ๋ณด์ ์ ์ฝ ์กฐ๊ฑด์ ์ ์ฉํ ํํฐ์ด๋ค
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and();
http.cors().configurationSource(corsConfigurationSource()); // CorsConfigurer ์ค์ ์ ์ด๊ธฐํ ํ๋ค
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
CORS ์์ฒญ์ ์ข ๋ฅ
Simple Request
- Simple Request ๋ ์๋น ์์ฒญ(Prefilght)์ ๊ณผ์ ์์ด ๋ฐ๋ก ์๋ฒ์ ๋ณธ ์์ฒญ์ ํ ํ, ์๋ฒ๊ฐ ์๋ต์ ํค๋์ Access-Control-Allow-Origin ๊ณผ ๊ฐ์ ๊ฐ์ ์ ์กํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ก ๋น๊ต ํ CORS ์ ์ฑ ์๋ฐ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํ๋ ๋ฐฉ์์ด๋ค
- ์ ์ฝ์ฌํญ
- GET, POST, HEAD ์ค์ ํ๊ฐ์ง Method๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค
- ํค๋๋ Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width Width ๋ง ๊ฐ๋ฅํ๊ณ Custom Header ๋ ํ์ฉ๋์ง ์๋๋ค
- Content-type ์ application/x-www-form-urlencoded, multipart/form-data, text/plain ๋ง ๊ฐ๋ฅํ๋ค
Preflight Request (์๋น์์ฒญ)
- ๋ธ๋ผ์ฐ์ ๋ ์์ฒญ์ ํ๋ฒ์ ๋ณด๋ด์ง ์๊ณ , ์๋น์์ฒญ๊ณผ ๋ณธ์์ฒญ์ผ๋ก ๋๋์ด ์๋ฒ์ ์ ๋ฌํ๋๋ฐ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋น์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ Preflight ๋ผ๊ณ ํ๋ฉฐ ์ด ์๋น์์ฒญ์ ๋ฉ์๋์๋ OPTIONS ๊ฐ ์ฌ์ฉ๋๋ค
- ์๋น์์ฒญ์ ์ญํ ์ ๋ณธ ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ ์ ๋ธ๋ผ์ฐ์ ์ค์ค๋ก ์์ ํ ์์ฒญ์ธ์ง ํ์ธํ๋ ๊ฒ์ผ๋ก ์์ฒญ ์ฌ์์ด Simple Request ์ ํด๋นํ์ง ์์ ๊ฒฝ์ฐ ๋ธ๋ผ์ฐ์ ๊ฐ Preflight Request ์ ์คํํ๋ค
RequestHeader ์์
- ๋ธ๋ผ์ฐ์ ๊ฐ ๋ณด๋ธ ์์ฒญ์ ๋ณด๋ฉด Origin์ ๋ํ ์ ๋ณด ๋ฟ๋ง ์๋๋ผ ์๋น ์์ฒญ ์ดํ์ ์ ์กํ ๋ณธ ์์ฒญ์ ๋ํ ๋ค๋ฅธ ์ ๋ณด๋ค๋ ํจ๊ป ํฌํจ๋์ด ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- ์ด ์๋น ์์ฒญ์์ ๋ธ๋ผ์ฐ์ ๋ Access-Control-Request-Headers ๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์ด ๋ณธ ์์ฒญ์์ Content-Type ํค๋๋ฅผ ์ฌ์ฉํ ๊ฒ์ ์๋ ค์ฃผ๊ฑฐ๋, Access-Control-Request-Method๋ฅผ ์ฌ์ฉํ์ฌ GET ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๊ฒ์ ์๋ฒ์๊ฒ ๋ฏธ๋ฆฌ ์๋ ค์ฃผ๊ณ ์๋ค
Response Header ์์
- ์๋ฒ๊ฐ ๋ณด๋ด์ค ์๋ต ํค๋์ ํฌํจ๋ Access-Control-Allow-Origin: https://security.io ์ ์๋ฏธ๋ ํด๋น URL ์ธ์ ๋ค๋ฅธ ์ถ์ฒ๋ก ์์ฒญํ ๊ฒฝ์ฐ์๋ CORS ์ ์ฑ ์ ์๋ฐํ๋ค๊ณ ํ๋จํ๊ณ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ด๊ณ ์๋ต์ ๋ฒ๋ฆฌ๊ฒ ๋๋ค
๋๊ธ๋จ๊ธฐ๊ธฐ