BackEnd/Spring Boot

[Spring] CORS ๊ฐœ๋…๊ณผ Spring Security 6 ์„ค์ • ํ†บ์•„๋ณด๊ธฐ

ddonghyeo 2024. 5. 19. 19:36

0. CORS

๊ฐœ๋ฐœํ•  ๋•Œ ์ •๋ง ๋งŽ์ด ๋งˆ์ฃผ์น˜๊ฒŒ ๋˜๋Š” CORS๋ฅผ ์ œ๋Œ€๋กœ ์•Œ์•„๋ณด์ž.

 

CORS (Cross-Origin Resource Sharing)

HTTP ํ—ค๋”๋ฅผ ์ด์šฉํ•˜์—ฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์ž์›์„ ๋‹ค๋ฅธ ์ถœ์ฒ˜์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ์ •์ฑ….

 

์—ฌ๊ธฐ์„œ '์ถœ์ฒ˜'๋ž€, ๋„๋ฉ”์ธ, ํ”„๋กœํ† ์ฝœ, ํฌํŠธ๋ฅผ ๋œปํ•œ๋‹ค.

 

์ž์›์„ ํ˜ธ์ถœํ•œ ํด๋ผ์ด์–ธํŠธ์™€ ์ž์›์„ ๋‚ด๋ฑ‰์€ ์‚ฌ์ดํŠธ๊ฐ€ ๋„๋ฉ”์ธ, ํ”„๋กœํ† ์ฝœ, ํฌํŠธ๊ฐ€ ๋‹ค๋ฅด๋‹ค๋ฉด "๋ธŒ๋ผ์šฐ์ €" ์—์„œ ๋ง‰๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

 

๋งŒ์•ฝ, CORS ์ •์ฑ…์ด ์—†๋‹ค๋ฉด..

ํ•ดํ‚น ์‹œ๋‚˜๋ฆฌ์˜ค #1
1. ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ค ํ•ดํ‚น์šฉ ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•œ๋‹ค.
2. ํ•ด๋‹น ํ•ดํ‚น์šฉ ์‚ฌ์ดํŠธ์—์„œ ์‹คํ–‰๋˜๋Š” js์—์„œ๋Š” ์‚ฌ์šฉ์ž ๋ธŒ๋ผ์šฐ์ €์— ์žˆ๋Š” ์ฟ ํ‚ค, ์„ธ์…˜ ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์— ์ธ์ฆ, ์ธ๊ฐ€๋ฅผ ์‹œ๋„ํ•œ๋‹ค.
3. ํ•ดํ‚น์šฉ ์‚ฌ์ดํŠธ๋Š” CORS ์ •์ฑ…์ด ์—†๋‹ค๋ฉด ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์–ป๋Š”๋‹ค.

 

๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ง‰์•„์ฃผ๋Š” ์•ˆ์ „ํ•œ ์ •์ฑ…์ด๋‹ค ^_^

 

ํ•œ ๋งˆ๋””๋กœ ์ •๋ฆฌํ•˜์ž๋ฉด,

 

 

์„œ๋ฒ„ ์ž…์žฅ์—์„œ ์ฃผ์†Œ, ํ”„๋กœํ† ์ฝœ, ํฌํŠธ๊ฐ€ ๋‹ฌ๋ผ๋„ ๋ฆฌ์†Œ์Šค๋ฅผ ํ—ˆ์šฉํ• ๊ฑด๋ฐ,

์–ด๋Š ๋„๋ฉ”์ธ์—์„œ ํ•ด๋‹น ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜๋Š”์ง€?๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ์ •์ฑ….

 

 

0-1. SOP(Same-Origin Policy)

 

๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์ด๋ผ๊ณ ๋„ ํ•˜๋ฉฐ, ๊ฐ™์€ ์ถœ์ฒ˜์—์„œ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ทœ์น™์„ ๊ฐ€์ง„๋‹ค.

 

์ตœ๊ทผ์—” ๊ฑฐ์˜ REST API๋ฅผ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›ฌ๋งŒํ•˜๋ฉด ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅธ ๊ฑด ์–ด์ฉ” ์ˆ˜ ์—†๋‹ค.

 

๊ฐ€์žฅ ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ์ด์   CORS ์ •์ฑ…์„ ์ง€ํ‚จ ๋ฆฌ์†Œ์Šค๋ฅผ ํ—ˆ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

 

 

 

 

1. Preflight

Preflight๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์—๊ฒŒ ๋ฏธ๋ฆฌ ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

ํด๋ผ์ด์–ธํŠธ๊ฐ€ OPTIONS Method๋กœ ๋จผ์ € ์„œ๋ฒ„์—๊ฒŒ ๋ฆฌ์†Œ์Šค ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•œ์ง€ ๋ฌป๊ฒŒ๋œ๋‹ค.

Preflight ์š”์ฒญ

 

 

preflight ๋ฐฉ์‹์œผ๋กœ OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ์„ ํ•œ ์ˆœ๊ฐ„์ด๋‹ค.

 

Request Header

Request Header๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

- Origin (์ถœ์ฒ˜) : ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ณธ์ธ์˜ ์ถœ์ฒ˜.

 

- Access-Control-Request-Method : ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” HTTP ๋ฉ”์„œ๋“œ.

 

- Access-Control-Request-Headers : ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ํ—ค๋”.

 

Response Header

 ๊ทธ์— ํ•ด๋‹นํ•œ Response Header๋ฅผ ์‚ดํŽด๋ณด์ž.

 

- Access-Control-Allow-Credentials : ์ž๊ฒฉ ์ฆ๋ช…(์ฟ ํ‚ค, ํ† ํฐ ๋“ฑ)์„ ํฌํ•จํ•œ ์š”์ฒญ์„ ํ—ˆ์šฉํ•  ์ง€ ์—ฌ๋ถ€.

true ๋˜๋Š” false.

ํ•ด๋‹น ํ—ค๋”๊ฐ€ true๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ, Access-Control-Allow-Origin์€ wildcard(*)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ  ๋ช…์‹œ์ ์ธ ์ถœ์ฒ˜๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•จ.

 

์‹ค์ œ๋กœ, credentials๋ฅผ true๋กœ ์„ค์ •ํ•˜๊ณ  Allow Origin์— ์™€์ผ๋“œ์นด๋“œ(*)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง„๋‹ค.

java.lang.IllegalArgumentException: When allowCredentials is true, 
allowedOrigins cannot contain the special value "*" 
since that cannot be set on the "Access-Control-Allow-Origin" response header. 
To allow credentials to a set of origins, 
list them explicitly or consider using "allowedOriginPatterns" instead.

 

- Access-Control-Allow-Headers : ํ•ด๋‹น ๋„๋ฉ”์ธ์— ๋Œ€ํ•ด ์„œ๋ฒ„๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” ํ—ค๋”.

 

- Access-Control-Allow-Methods : ํ•ด๋‹น ๋„๋ฉ”์ธ์— ๋Œ€ํ•ด ์„œ๋ฒ„๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” HTTP ๋ฉ”์„œ๋“œ. 

 

- Access-Control-Allow-Origin : ์„œ๋ฒ„์—์„œ ํ—ˆ์šฉํ•œ Origin(์ถœ์ฒ˜)

 

- max-age : Preflight๋Š” ์š”์ฒญ์„ ๋‘ ๋ฒˆ ๋ณด๋‚ด๋ฉด์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ ์ž ๋ธŒ๋ผ์šฐ์ €๋Š” ์ฒซ ์š”์ฒญ์— ์บ์‹œ๋ฅผ ์ €์žฅํ•ด๋‘”

๋‹ค. ์„œ๋ฒ„๋Š” ์ด ์บ์‹œ๋ฅผ ์ €์žฅํ•  ์‹œ๊ฐ„์„ ์ง€์ •ํ•ด์ค€๋‹ค.

 

 

2. Simple Reqeust

 

Simple Request ๋ฐฉ์‹์€ Preflight๋ฅผ ์„ ํ–‰ํ•˜์ง€ ์•Š๊ณ  ์š”์ฒญ์„ ์ฆ‰์‹œ ์ „์†กํ•œ๋‹ค.

 

ํ•˜์ง€๋งŒ ์กฐ๊ฑด์ด ์žˆ๋‹ค.

 

1. HTTP ๋ฉ”์„œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ GET, POST, HEAD์—ฌ์•ผ ํ•œ๋‹ค.

 

2. Content-Type์€ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค.

- application/x-www-form-unlencoded

- multipart/form-data

- text/plain

 

๋ณดํ†ต ์ธ๊ฐ€๋ฅผ ์œ„ํ•œ Authorization์ด๋‚˜, REST API์šฉ application/json์„ ์ •์˜ํ•  ์ˆ˜ ์—†๋‹ค.

๋”ฐ๋ผ์„œ ์ผ๋ถ€ ๊ฒฝ์šฐ์—๋งŒ ์“ฐ์ธ๋‹ค.

 

3. Reqeust ํ—ค๋”๋Š” ๋‹ค์Œ๋งŒ ํ—ˆ์šฉ๋œ๋‹ค.

- Accept

- Accept-Langugage

- Content-Language

- Content-Type

 

 

 

3. CorsConfig

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ Spring Boot์—์„œ Cors๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

 

*Spring Boot 3, Spring Security 6.x.x ๊ธฐ์ค€

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CorsConfig {

    public static CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();

        //๋ฆฌ์†Œ์Šค๋ฅผ ํ—ˆ์šฉํ•  URL ์ง€์ •
        ArrayList<String> allowedOriginPatterns = new ArrayList<>();
        allowedOriginPatterns.add("http://localhost:5000");
        allowedOriginPatterns.add("http://127.0.0.1:5000");
        configuration.setAllowedOrigins(allowedOriginPatterns);

        //ํ—ˆ์šฉํ•˜๋Š” HTTP METHOD ์ง€์ •
        ArrayList<String> allowedHttpMethods = new ArrayList<>();
        allowedHttpMethods.add("GET");
        allowedHttpMethods.add("POST");
        allowedHttpMethods.add("PUT");
        allowedHttpMethods.add("DELETE");
        configuration.setAllowedMethods(allowedHttpMethods);

        configuration.setAllowedHeaders(Collections.singletonList("*"));
//        configuration.setAllowedHeaders(List.of(HttpHeaders.AUTHORIZATION, HttpHeaders.CONTENT_TYPE));

        //์ธ์ฆ, ์ธ๊ฐ€๋ฅผ ์œ„ํ•œ credentials ๋ฅผ TRUE๋กœ ์„ค์ •
        configuration.setAllowCredentials(true); 

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

 

๋จผ์ €, ๋‚˜๋Š” CorsConfig ์ •์ฑ… ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  config๋ฅผ returnํ•˜๋Š” static ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

 

๊ฐ ํ—ˆ์šฉํ•  ํŒจํ„ด๋“ค์€ ๋ณดํ†ต String ๊ฐ’์œผ๋กœ ์ฃผ์ž…ํ•˜๋ฉด ๋œ๋‹ค.

 

CORS ์„ค์ • ํด๋ž˜์Šค ํ•„๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

 

public class CorsConfiguration {

	@Nullable
	private List<String> allowedOrigins;

	@Nullable
	private List<OriginPattern> allowedOriginPatterns;

	@Nullable
	private List<String> allowedMethods;

	@Nullable
	private List<HttpMethod> resolvedMethods = DEFAULT_METHODS;

	@Nullable
	private List<String> allowedHeaders;

	@Nullable
	private List<String> exposedHeaders;

	@Nullable
	private Boolean allowCredentials;

	@Nullable
	private Boolean allowPrivateNetwork;

	@Nullable
	private Long maxAge;
    
    //...
}

 

ํ•„์š”ํ•œ ์„ค์ •์„ ์ฃผ์ž…ํ•ด๋‘๊ณ , Security Filter Chain์— ์„ค์ •์„ ๋“ฑ๋กํ•ด๋‘๋ฉด ๋œ๋‹ค.

 

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

	//...

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // CORS ์ •์ฑ… ์„ค์ •
        http
                .cors(cors -> cors
                        .configurationSource(CorsConfig.corsConfigurationSource())
                );
    }
}

 

 

๋งŒ์•ฝ ์—ฌ๊ธฐ์„œ ๊ฒฝ๋กœ๋ณ„ ๋‹ค๋ฅธ CORS๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

 

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	@Order(0)
	public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/api/**")
			.cors((cors) -> cors
				.configurationSource(apiConfigurationSource())
			)
			...
		return http.build();
	}

	@Bean
	@Order(1)
	public SecurityFilterChain myOtherFilterChain(HttpSecurity http) throws Exception {
		http
			.cors((cors) -> cors
				.configurationSource(myWebsiteConfigurationSource())
			)
			...
		return http.build();
	}

	CorsConfigurationSource apiConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Arrays.asList("https://api.example.com"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

	CorsConfigurationSource myWebsiteConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

}

 

 

์—ฌ๊ธฐ์„œ @Order๋ฅผ ํ†ตํ•ด ๋นˆ์˜ ๋กœ๋“œ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@Order

@Order ์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์€ LOWEST_PRECEDENCE์ด๋‹ค.

 

 

 

 

Order์€ ๋‚ฎ์€ ๊ฐ’์ด ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ–๋Š”๋ฐ, LOWEST_PRECEDENCE๋Š” Integer์˜ MAX๊ฐ’์ด๋‹ค.

 

์ฆ‰, ๊ธฐ๋ณธ๊ฐ’์€ ๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„์ด๋‹ค.

 

 

๋งŒ์•ฝ ๋™์ผํ•œ ์ˆœ์„œ ๊ฐ’์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋“ค์ด๋ฉด ์ž„์˜์˜ ์ •๋ ฌ ์œ„์น˜๋ฅผ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

 

 

๋”ฐ๋ผ์„œ, ์œ„ ์˜ˆ์‹œ๋Š” api ์—”๋“œํฌ์ธํŠธ ๋ถ€๋ถ„์„ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋†’๊ฒŒ(0) ์žก์•„์„œ ๋นˆ์— ๋จผ์ € ๋กœ๋“œ๋˜๋„๋ก ํ•œ๋‹ค.

 

 

 

๋ฒˆ์™ธ

 

CORS๋Š” ๋ธŒ๋ผ์šฐ์ € ์ž์ฒด์—์„œ ๋ง‰๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ธŒ๋ผ์šฐ์ € ์„ค์ •์„ ํ†ตํ•ด ์ •์ฑ…์„ ๋น„ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Chrome Extension ์ค‘ CORS Unblock์„ ์ด์šฉํ•˜๋ฉด ์ž„์˜๋กœ CORS๋ฅผ ์ผœ๊ณ  ๋Œ ์ˆ˜ ์žˆ๋‹ค.

 

https://chromewebstore.google.com/detail/cors-unblock/lfhmikememgdcahcdlaciloancbhjino?hl=ko

 

CORS Unblock

No more CORS error by appending 'Access-Control-Allow-Origin: *' header to local and remote web requests when enabled

chromewebstore.google.com

 

 

CORS๋Š” ๋ณด์•ˆ์„ ์ง€์ผœ์ฃผ๋Š” ๊ณ ๋งˆ์šด ์ •์ฑ…์ด๋‹ˆ ํ…Œ์ŠคํŠธ์šฉ์ด ์•„๋‹ˆ๋ผ๋ฉด ๊ผญ ๊บผ๋‘์ž ^_^

 

 

๋” ์ž์„ธํ•œ ๋‚ด์šฉ

https://ddonghyeo.tistory.com/58

 

Postman์€ ๋™์ž‘ํ•˜์ง€๋งŒ Swagger๋Š” ์•ˆ๋๋˜ ์ด์œ 

๋˜ CORS ๋„ˆ์•ผ?1. ๋ฌธ์ œ๋‚˜๋Š” Spring Cloud ๊ธฐ๋ฐ˜ API ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๊ณ , Swagger๋ฅผ ์ด์šฉํ•˜์—ฌ API ๋ช…์„ธ์„œ๋ฅผ ๋ฐฐํฌํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ..  ์Šค์›จ๊ฑฐ์—์„œ ์™œ์ธ์ง€ 403 Forbidden์„ ๋ฐ›์•˜๋‹ค.  Gateway์˜ ๋กœ๊ทธ๋ฅผ ์‚ดํŽด๋ณด์•˜๋”๋‹ˆ,PRE Filter

ddonghyeo.tistory.com

ํ•ด๋‹น ๊ธ€์—์„œ๋Š” CORS๊ฐ€ ์ •ํ™•ํžˆ ์–ด๋–ค ํ•„ํ„ฐ๋ฅผ ํ†ตํ•ด ๋™์ž‘ํ•˜๊ณ , ์–ด๋–ป๊ฒŒ ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.