๐ ํ์ฌ ์ํฉ ๋ฐ ๋ฐฐ๊ฒฝ ์ค๋ช
๐ฝ SecurityConfig API ์์ฒญ ํ๋ฆ
ํ์ฌ API ์์ฒญ์ ๊ณต๊ฐ ์ ๊ทผ ํํฐ ์ฒด์ธ๊ณผ ์ธ์ฆ์ด ํ์ํ ํํฐ ์ฒด์ธ, ์ด๋๋ฏผ ๊ถํ ํํฐ ์ฒด์ธ์ ์์ฐจ์ ์ผ๋ก ๊ฑฐ์น๊ฒ ๋์ด์๋ค.
- ๊ณต๊ฐ ์ ๊ทผ ํํฐ ์ฒด์ธ: ๋ก๊ทธ์ธ ์์ด๋ ์ ๊ทผ ๊ฐ๋ฅํ ์๋ํฌ์ธํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
- ์ธ์ฆ์ด ํ์ํ ํํฐ ์ฒด์ธ: ๋ก๊ทธ์ธ์ด ๋ฐ๋์ ์๊ตฌ๋๋ ์๋ํฌ์ธํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
- ์ด๋๋ฏผ ๊ถํ ํํฐ ์ฒด์ธ: ์ด๋๋ฏผ๋ง ์ ๊ทผํ ์ ์๋ ์๋ํฌ์ธํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
๐ฝ CORS ์ค์
Spring Security์์ CORS๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๊ฐ ์๋ค.
- CorsFilter Bean ๋ฑ๋ก
- CorsConfigurationSource Bean ๋ฑ๋ก
ํ์ฌ ํ๋ก์ ํธ์์๋ CorsConfigurationSource๋ฅผ ์ฌ์ฉํ์ฌ CORS๋ฅผ ์ค์ ํ๋ค.
โถ ๊ตฌํ ์ฝ๋
๐ฝ SecurityConfig ํด๋์ค: ํํฐ ์ฒด์ธ
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
...
// ๊ณต๊ฐ ์ ๊ทผ ํํฐ ์ฒด์ธ
@Bean
@Order(1)
public SecurityFilterChain publicFilterChain(HttpSecurity http) throws Exception {
defaultSecuritySetting(http);
http
.securityMatchers(matcher -> matcher
.requestMatchers(publicRequestMatchers()))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(publicRequestMatchers()).permitAll()
.anyRequest().authenticated()
);
return http.build();
}
// ์ธ์ฆ ๋ฐ ๊ถํ์ด ํ์ํ ํํฐ ์ฒด์ธ
@Bean
@Order(2)
public SecurityFilterChain authenticatedFilterChain(HttpSecurity http) throws Exception {
defaultSecuritySetting(http);
http
.securityMatchers(matcher -> matcher
.requestMatchers(authenticatedRequestMatchers()))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(authenticatedRequestMatchers())
.hasAnyAuthority(RoleType.ROLE_USER.name(), RoleType.ROLE_ADMIN.name())
.anyRequest().authenticated())
.exceptionHandling(exception -> exception
.accessDeniedHandler(customAccessDeniedHandler))
.addFilterBefore(new JwtFilter(jwtService), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
// ์ด๋๋ฏผ ๊ถํ์ด ํ์ํ ํํฐ ์ฒด์ธ
@Bean
@Order(3)
public SecurityFilterChain adminFilterChain(HttpSecurity http) throws Exception {
defaultSecuritySetting(http);
http
.securityMatchers(matcher -> matcher
.requestMatchers(adminRequestMatchers()))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(adminRequestMatchers())
.hasAuthority(RoleType.ROLE_ADMIN.name())
.anyRequest().authenticated())
.exceptionHandling(exception -> exception
.accessDeniedHandler(customAccessDeniedHandler))
.addFilterBefore(new JwtFilter(jwtService), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
// ๊ณต๊ฐ ์ ๊ทผ endpoint
private RequestMatcher[] publicRequestMatchers() { ... }
// ์ธ์ฆ ๋ฐ ๊ถํ์ด ํ์ํ endpoint
private RequestMatcher[] authenticatedRequestMatchers() { ... }
// ์ด๋๋ฏผ ๊ถํ์ด ํ์ํ endpoint
private RequestMatcher[] adminRequestMatchers() { ... }
๐ฝ SecurityConfig ํด๋์ค: CORS ์ค์
// Spring Security ๊ธฐ๋ณธ ์ค์ ๋ฉ์๋
private void defaultSecuritySetting(HttpSecurity http) throws Exception {
http
// CORS ์ค์
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
...
}
// CORS ์ค์
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods( ... );
configuration.setAllowedHeaders( ... );
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
๐จ ๋ฌธ์ ์ํฉ
โถ 1. Allow Origin์ ์์ผ๋์นด๋(*) ์ฌ์ฉ ๊ด๋ จ ์๋ฌ
CORS ์ค์ ์์ ํ์ฉ ์ถ์ฒ(Allow Origin)์ ์์ผ๋์นด๋(*)๋ฅผ ์ฌ์ฉํ๋๋, ๋ชจ๋ ์์ฒญ์์ CORS ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ์๋ฒ ๋ก๊ทธ๋ฅผ ํ์ธํ ๊ฒฐ๊ณผ, ๋ค์๊ณผ ๊ฐ์ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ธํ ์ ์์๋ค.
โถ 2. Preflight ์์ฒญ ์๋ฌ
CORS๋ฅผ ์ค์ ํ ์ดํ, ๋ธ๋ผ์ฐ์ ์์ Preflight ์์ฒญ(OPTIONS ๋ฉ์๋) ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
โถ 3. Preflight ์ค์ ์ดํ์๋ ์ฌ์ ํ ๋ฐ์ํ๋ Preflight ์์ฒญ ์๋ฌ
Preflight ์์ฒญ ์ฒ๋ฆฌ๋ฅผ ์ค์ ํ ์ดํ์๋, ๊ณ์ํด์ Prefiglht ์์ฒญ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฅผ ํ์ธํ๊ธฐ ์ํด JwtFilter์์ OPTIONS ๋ฉ์๋๊ฐ ๋๋ฌํ ๊ฒฝ์ฐ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋๋ก ์ค์ ํ์ผ๋, ํด๋น ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋์ง ์์๋ค. ์ด๋ฅผ ํตํด Preflight ์์ฒญ์ด ์ ์ด์ Spring Security ํํฐ ์ฒด์ธ์ ๋๋ฌํ์ง ์์๋ค๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
์ ์์ ์ธ ๊ฒฝ์ฐ์๋ ์๋์ ๊ฐ์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋์ด์ผ ํ๋ค.
โ๏ธ ์์ธ ๋ถ์
โถ 1. Allow Origin์ ์์ผ๋์นด๋(*)๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋ ์ด์ ?
CORS ์ค์ ์์ Access-Control-Allow-Origin ํค๋์ ์์ผ๋์นด๋(*)๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ชจ๋ ๋๋ฉ์ธ์์์ ์์ฒญ์ ํ์ฉํ ์ ์๋ค. ๊ทธ๋ฌ๋ Access-Control-Allow-Credentials: true๊ฐ ์ค์ ๋ ์์ฒญ์์๋ ์์ผ๋์นด๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์์ผ๋์นด๋๋ ๋ชจ๋ ๋๋ฉ์ธ์์ ์์ฒญ์ ํ์ฉํ๊ธฐ ๋๋ฌธ์, ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ API์ ๊ฒฝ์ฐ ๋ณด์ ์ทจ์ฝ์ ์ผ๋ก ์ด์ด์ง ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฆ, ์ธ์ฆ ์ ๋ณด๊ฐ ํฌํจ๋ ์์ฒญ์์๋ ๋ช ์์ ์ผ๋ก ํ์ฉ๋ Origin์ ์ง์ ํด์ผ ํ๋ค.
โถ 2. Preflight ์์ฒญ ์๋ฌ๊ฐ ๋ฐ์ํ ์ด์
Preflight
๋ธ๋ผ์ฐ์ ๊ฐ CORS ์ ์ฑ ์ ์ ์ฉํ ๋ ๋ณด์์์ ์ด์ ๋ก ์ค์ ์์ฒญ ์ ์ ์๋ฒ์ ํ์ธ ์์ฒญ์ ๋ณด๋ด๋ ๋ฉ์ปค๋์ฆ์ผ๋ก, HTTP OPTIONS ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
๐ฝ Preflight ์์ฒญ ์๋ฌ๊ฐ ๋ฐ์ํ ์ด์
Spring ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด, CORS๋ Spring Security๋ณด๋ค ๋จผ์ ์ฒ๋ฆฌ๋์ด์ผ ํ๋ค๊ณ ๋ช ์๋์ด ์๋ค. ๊ทธ ์ด์ ๋ Preflight ์์ฒญ์ด ์ฟ ํค(JESESSIONID)๋ ์ธ์ฆ ์ ๋ณด๋ฅผ ํฌํจํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. Preflight ์์ฒญ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ฒ์ CORS ์ ์ฑ ์ ํ์ธํ๊ธฐ ์ํด ๋ณด๋ด๋ ํน์ํ ์์ฒญ์ผ๋ก, ๋น์ฆ๋์ค ๋ก์ง์ด๋ ์ธ์ฆ/๊ถํ ๊ฒ์ฌ๋ฅผ ์ํ ์์ฒญ์ด ์๋๋ค.
๋ง์ฝ Spring Security๊ฐ ๋จผ์ ์ค์ ๋ ๊ฒฝ์ฐ, Spring Security๊ฐ Preflight ์์ฒญ๋ ์ผ๋ฐ ๋น์ฆ๋์ค ์์ฒญ์ผ๋ก ๊ฐ์ฃผํด ํํฐ ์ฒด์ธ์ ํต๊ณผํ์ง ๋ชปํ๋ค. ์ด๋ Preflight ์์ฒญ์ด ์ด๋ ํ ํํฐ ์ฒด์ธ์ ์๋ํฌ์ธํธ์๋ ๋งค์นญ๋์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก, Preflight ์์ฒญ์ ๋จ์ํ ๋ธ๋ผ์ฐ์ ๊ฐ CORS ์ ์ฑ ์ ํ์ธํ๊ธฐ ์ํด ๋ณด๋ด๋ ์์ฒญ์ด๋ฏ๋ก, ๋น์ฆ๋์ค ๋ก์ง์ด๋ ์ธ์ฆ/๊ถํ ๊ฒ์ฌ ๋์์ด ๋์ด์๋ ์ ๋๊ธฐ ๋๋ฌธ์ CORS ์ฒ๋ฆฌ๋ Spring Security๋ณด๋ค ์ฐ์ ์ ์ผ๋ก ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค.
๐ฝ Spring Security์์ Preflight ์์ฒญ ์ค์ ๋ฐฉ๋ฒ: ์๋ชป๋ ๋ฐฉ๋ฒ
์๋๋ Preflight ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ค์ ๋ฐฉ๋ฒ์ด๋ค.
public class SecurityConfig {
...
// Spring Security ๊ธฐ๋ณธ ์ค์ ๋ฉ์๋
private void defaultSecuritySetting(HttpSecurity http) throws Exception {
http
// CORS ์ค์
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// Preflight ์ค์ (ํ๋ฆฐ ์ค์ ์ด๋ ์ฐธ๊ณ X)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll())
}
}
๊ทธ๋ฌ๋, ์ด๋ ํ๋ฆฐ ์ค์ ๋ฐฉ๋ฒ์ด๋ค. 3๋ฒ์์ ์ด ์ค์ ์ ๋ฌธ์ ์ ๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ ํ์ธํ์.
โถ 3. Preflight ์ค์ ์ดํ์๋ ์ฌ์ ํ Preflight ์์ฒญ ์๋ฌ๊ฐ ๋ฐ์ํ ์ด์
๐ฝ CorsFilter๊ฐ ์ธ์ฆ ํํฐ ์ดํ์ ์คํ๋๊ณ ์๋๊ฐ?
cors() ๋ฉ์๋๋ ๋ด๋ถ์ ์ผ๋ก CorsFilter๋ฅผ ์์ฑํ๋ค. ์ด๋ฅผ ํ์ธํ๊ธฐ ์ํด ์ง์ ํด๋น ํด๋์ค๋ฅผ ๋ถ์ํ๋ค.
์ฒ์์๋ CorsFilter๊ฐ ์ธ์ฆ์ ์๊ตฌํ๋ ํํฐ๋ณด๋ค ๋ค์์ ์คํ๋๊ณ ์์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ผ๋ก ์ถ์ธกํ๋ค. ์๋๋ Spring Security์์ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ ํํฐ๋ค์ด๋ค.
ํํฐ ์ฒด์ธ์ด ์ด๊ธฐํ๋ ๋, ๋ฑ๋ก๋ ํํฐ์ ๊ทธ ์คํ ์์๋ฅผ ํ์ธํ๊ธฐ ์ํด ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ์ฌ ์ถ๋ ฅํด ๋ณด์๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก, ์ธ์ฆ์ ์๊ตฌํ๋ ๋ชจ๋ ํํฐ๊ฐ CorsFilter ๋ค์์ ์คํ๋๊ณ ์์์์ ํ์ธํ๋ค. ๋ฐ๋ผ์, ์ด๋ฒ ๋ฌธ์ ๋ ํํฐ ์์์ ๋ฌธ์ ๋ ์๋์์์ ์ ์ ์์๋ค.
๐ฝ Spring Security์์ Preflight ์์ฒญ ์ค์ ๋ฐฉ๋ฒ
Spring Security ๊ณต์ ๋ฌธ์๋ฅผ ์ ๋ ํ ๊ฒฐ๊ณผ, ์์ธ์ ํ์ ํ ์ ์์๋ค.
๋ฌธ์ ๋ securityMatchers()์ authorizeHttpRequests()์ ์ฐจ์ด๋ฅผ ์ ๋๋ก ์ดํดํ์ง ๋ชปํ๊ณ ์ฌ์ฉํ ๋ฐ ์์๋ค.
Spring Security๋ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ ๋ ํํฐ ์ฒด์ธ์ ํ์ฉํ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฌ๋ฌ ๊ฐ์ ํํฐ ์ฒด์ธ์ ์ ์ํ ์ ์๋ค. ๊ฐ ํํฐ ์ฒด์ธ์ ํน์ ์์ฒญ ๊ฒฝ๋ก์๋ง ์ ์ฉ๋๋ฉฐ, ์ด ์์ฒญ ๊ฒฝ๋ก์ ํํฐ ์ฒด์ธ์ ์ฐ๊ฒฐํ๋ ์ญํ ์ securityMatchers()์ authorizeHttpRequests()๊ฐ ์ํํ๋ค.
- securityMatchers()
- ํน์ ํํฐ ์ฒด์ธ์ด ์ ์ฉ๋ ์์ฒญ์ ๊ฒฐ์ ํ๋ค.
- ์ฆ, ํน์ ํํฐ ์ฒด์ธ์ด ์์ฒญ์ ์ฒ๋ฆฌํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ์ง๋ ๊ธฐ์ค์ด๋ค.
- ์์ฒญ์ด securityMatchers() ์กฐ๊ฑด๊ณผ ์ผ์นํ์ง ์์ผ๋ฉด, ํด๋น ํํฐ ์ฒด์ธ์ ์์ ์คํ๋์ง ์๋๋ค.
- authorizeHttpRequests()
- ํํฐ ์ฒด์ธ ๋ด๋ถ์์ ์์ฒญ ํ์ฉ/๊ฑฐ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ค.
- ์ฆ, ํํฐ ์ฒด์ธ์ด ์ด๋ฏธ ์ ํ๋ ์ํฉ์์, ์์ฒญ์ ํ์ฉํ ์ง/๊ฑฐ๋ถํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ค.
- ์์ฒญ์ด ํํฐ ์ฒด์ธ์ ๋งค์นญ ์กฐ๊ฑด์ ํต๊ณผํ ์ดํ์ ๋์์ ์ ์ํ๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก ์์ฒญ์ด Spring Security์ ํํฐ ์ฒด์ธ์ ๋๋ฌํ๋ ค๋ฉด ๋จผ์ securityMatchers()์ ์กฐ๊ฑด๊ณผ ์ผ์นํด์ผ ํ๋ค. ํ์ง๋ง ๋ฌธ์ ๋ Preflight ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ค์ ์ด securityMatchers()๊ฐ ์๋, authorizeHttpRequests()์ ์์ฑ๋์ด ์์๋ ๊ฒ์ด๋ค. ์ด๋ก ์ธํด Preflight ์์ฒญ์ด ํํฐ ์ฒด์ธ์ ๋๋ฌํ์ง ๋ชปํด ๋ฌธ์ ๊ฐ ๋ฐ์ํ์๋ค.
๊ตฌ๊ธ๋ง์ ํตํด authorizeHttpRequests()๋ก Preflight ์์ฒญ์ ์ฒ๋ฆฌํ๋ค๋ ๊ธ์ด ๋ง์ด ์์ด ์ด๋ฅผ ์ ์ฉํด ๋ณด์์ผ๋, ์๋ํ์ง ์์๋ค. ์ถํ ์กฐ์ฌํด ๋ณด๋, Spring Security 5.5 ๋ฒ์ ๋ถํฐ securityMatchers()์ authorizeHttpRequests()์ ์ญํ ์ด ๋ ๋ช ํํ๊ฒ ๊ตฌ๋ถ๋์๋ค๊ณ ํ๋ค. (์ด ์ ๋ณด๋ ๋ช ํํ์ง ์์ผ๋ฏ๋ก ์ฐธ๊ณ ๋ง ๋ฐ๋๋ค.)
๐จ ํด๊ฒฐ ๋ฐฉ๋ฒ
โถ Allow Origin์ ์์ผ๋์นด๋(*) ์ฌ์ฉ ๊ธ์ง
๐ฝ ๋ณ๊ฒฝ ์
// CORS ์ค์
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*")); // ์์ผ๋์นด๋ ์ฌ์ฉ
configuration.setAllowedMethods( ... );
configuration.setAllowedHeaders( ... );
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return source;
}
๐ฝ ๋ณ๊ฒฝ ํ
// CORS ์ค์
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
// ๋ช
์์ ์ธ Origin ์ฌ์ฉ
configuration.setAllowedOrigins(
Arrays.asList(
"http://localhost:8080",
"http://127.00.1:8080",
"http://localhost:5173",
"http://127.0.0.1:5173",
...
)
);
configuration.setAllowedMethods( ... );
configuration.setAllowedHeaders( ... );
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return source;
}
โถ CORS ์ ์ฉ - CorsConfigurationSource ๋ฐฉ์: Preflight ์์ฒญ ํ์ฉ
๐ฝ ๋ณ๊ฒฝ ์
// Spring Security ๊ธฐ๋ณธ ์ค์ ๋ฉ์๋
private void defaultSecuritySetting(HttpSecurity http) throws Exception {
http
// CORS ์ค์
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// authorizeHttpRequests()์ Preflight ํ์ฉ ์ค์ ์ถ๊ฐ
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll())
...
}
๐ฝ ๋ณ๊ฒฝ ํ
// Spring Security ๊ธฐ๋ณธ ์ค์ ๋ฉ์๋
private void defaultSecuritySetting(HttpSecurity http) throws Exception {
http
// CORS ์ค์
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// securityMatchers()์ Preflight ํ์ฉ ์ค์ ์ถ๊ฐ
.securityMatchers(authorize -> authorize
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll())
...
}
โถ CORS ์ ์ฉ - CorsFilter ๋ฐฉ์
์ฌ์ค ๊ฐ์ฅ ์ฝ๊ณ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ CorsFilter๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. ๊ณต์ ๋ฌธ์์์๋ ์ด ๋ฐฉ์์ ๊ฐ์ฅ ๊ถ์ฅํ๊ณ ์๋ค. ์ด ๋ฐฉ์์ ๊ท์ฐฎ๊ฒ Preflight ์์ฒญ ์ค์ ์ ๋ฐ๋ก ํด์ฃผ์ง ์์๋ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค๋ค.
๐ฝ CorsFilter์์ ์ฌ์ฉํ๋ DefaultCorsProcessor์ Preflight ์์ฒญ ์ฒ๋ฆฌ ํ์ธ
CorsFilter์์ ์ฌ์ฉํ๋ DefaultCorsProcessor๋ฅผ ์ง์ ํ์ธํ์ฌ, Preflight ์์ฒญ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋์ง ์ง์ ํ์ธํด ๋ณด์๋ค. DefaultCorsProcessor ๋ด๋ถ ์ฝ๋๋ฅผ ์ดํด๋ณด๋, CorsUtils.isPreFlightRequest() ๋ฉ์๋๋ฅผ ํตํด Preflight ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
๐ฝ ์ค์ ๋ฐฉ๋ฒ
์๋๋ CorsFilter ์ค์ ์์๋ค.
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
Arrays.asList(
"http://localhost:8080",
"http://127.0.0.1:8080",
"http://localhost:5173",
"http://127.0.0.1:5173",
...
)
);
configuration.setAllowedMethods( ... );
configuration.setAllowedHeaders( ... );
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return new CorsFilter(source);
}
}
์ ํด๋์ค๋ง ์ถ๊ฐํ๋ฉด, SecurityConfig์์ ๋ณ๋์ CORS ์ค์ ์ ํ์ง ์์๋ Spring MVC๊ฐ Bean์ผ๋ก ๋ฑ๋ก๋ CorsFilter๋ฅผ ์๋์ผ๋ก ๊ฐ์งํ์ฌ CORS ์ค์ ์ ์ ์ฉํ๋ค.
Spring MVC์ CORS ์ค์ ์ฐ์ ์์
1. CorsFilter๊ฐ Bean์ผ๋ก ๋ฑ๋ก๋์ด ์์ ๊ฒฝ์ฐ
2. CorsConfigurationSource๊ฐ Bean์ผ๋ก ๋ฑ๋ก๋์ด ์์ ๊ฒฝ์ฐ
3. Spring MVC๊ฐ ํด๋์ค ํจ์ค์ ์์ผ๋ฉด Spring MVC์ HandlerMappingIntrospector๊ฐ CORS ์ค์ ์ ์ถ๋ก ํ์ฌ ์ฌ์ฉ
์ฃผ์ ์ฌํญ
1. ์๋ํ์ง ์์ ๊ฒฝ์ฐ, ํํฐ ์ฒด์ธ์ http.addFilter(corsConfig.corsFilter())๋ฅผ ์ง์ ์ถ๊ฐํด์ผ ํ ์ ์๋ค.
2. CorsFilter์ CorsConfigurationSource๊ฐ ๋ ๋ค Bean์ผ๋ก ๋ฑ๋ก๋์ด ์์ ๊ฒฝ์ฐ ์ถฉ๋์ด ๋ฐ์ํ ์ ์๋ค.
๐ ๊ฒฐ๊ณผ ๊ด์ฐฐ
Preflight ์์ฒญ(OPTIONS ๋ฉ์๋)์ด ์ ์์ ์ผ๋ก ํํฐ๋ฅผ ํต๊ณผํ๋ฉฐ, ์๋ํฌ์ธํธ๋ค์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
๐ก ๊ณ ์ฐฐ
- ๋๋์ด ํ๋ก์ ํธ ๋ด๋ด ๊ณจ๋จธ๋ฆฌ๋ฅผ ์๊ฒ ํ๋ CORS ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ , ๊ทธ ๊ทผ๋ณธ์ ์ธ ์์ธ์ ํ์ ํ๋ ๋ฐ ์ฑ๊ณตํ๋ค. ์์ฒญ๋๊ฒ ๋ง์ ์ฝ์ง์ ํ์ง๋ง, ๊ทธ ๊ณผ์ ์์ Spring Security์ CORS์ ๋ํด ๊น์ด ๋ฐฐ์ฐ๋ ๊ณ๊ธฐ๊ฐ ๋์๋ค. ์ด ๋ฌธ์ ๊ฐ ๊ตฌ๊ธ๋ง์ ํด๋ ๋ ํผ๋ฐ์ค๊ฐ ๋ง์ง ์์, ๋น์ทํ ๋ฌธ์ ๋ฅผ ๊ฒช๋ ๋๊ตฐ๊ฐ์๊ฒ ๋์์ด ๋ ๊น ์ถ์ด ์์ธํ ์ ๋ฆฌํด ๊ธ์ ๋จ๊ธฐ๊ณ ์ ํ๋ค.
- ์ฌ์ค ํ๋ก์ ํธ ์งํ ์๊ฐ์ด ๋ถ์กฑํ๋ค ๋ณด๋, ๋ฌธ์ ์ ์์ธ์ ์ ํํ ํ์ ํ์ง ๋ชปํ ์ฑ ํด๊ฒฐ ๋ฐฉ๋ฒ๋ง ์ฌ๋ฌ ๊ฐ ๋์ ํด ์๋ฌ๋ฅผ ์ ์ ํด๊ฒฐํด ๋จ์๋ค. ํ์ง๋ง ์ ์ ์์ธ์ ์์ง ๋ชปํ ์ํ๋ก ๋์ด๊ฐ๋ ๊ฒ์ด ๋ ์ฐ์ฐํ๊ฒ ๋จ์ ์์๋ค. ํ๋ก์ ํธ๊ฐ ๋๋ ์ดํ ํธ๋ฌ๋ธ์ํ ์ ํตํด ๊ฒฐ๊ตญ ์์ธ์ ํ์ ํ ์ ์์๊ณ , ๊ทธ๋์์ ์ฝ์ง์ด ํ๋์ง ์์์์ ๊นจ๋ฌ์ผ๋ฉฐ ๋ฟ๋ฏํจ์ ๋๊ผ๋ค.
- ์ด๋ฒ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ณผ์ ์์ Spring ๊ณต์ ๋ฌธ์๋ฅผ ๋ฐํ์ผ๋ก ์์ธ์ ๋ถ์ํ๋ฉฐ, ๊ณต์ ๋ฌธ์๋ฅผ ๊ผผ๊ผผํ ํ์ธํ๋ ๊ฒ์ ์ค์์ฑ์ ๋ค์ ํ ๋ฒ ์ค๊ฐํ๋ค. ์ต๊ทผ GPT์ ์์กดํ๋ ์ ์ ๋ฐ์ฑํ๋ ๊ณ๊ธฐ๊ฐ ๋์๋ค.
๐ ์ฐธ๊ณ ์๋ฃ
- Spring ๊ณต์ ๋ฌธ์: CORS
- Spring ๊ณต์ ๋ฌธ์: cors()
- Spring ๊ณต์ ๋ฌธ์: Choosing securityMatcher or requestMatchers
- Spring์ผ๋ก ๊ฐ๋ฐํ๋ฉฐ ๋ง๋ฅ๋จ๋ฆฐ CORS!
- [์ด์ํด๊ฒฐ] CORS with Spring (MVC, Security)
- springboot 35๊ฐ - Security Cors ์ค์ (์ ๋ฆฌ)
- [Spring Security] Spring Security Filter Chain ์ ๋ํด