Spring boot, Spring cloud gateway, 그리고 react 간의 cors 해결기

작성자 : 조회수 :


제목 : Spring boot, Spring cloud gateway, 그리고 react 간의 cors 해결기

설명 : [Springboot,SpringCloudGateway,React]그간 머리 아프게 했던 cors문제를 해결하기 위한 과정을 적었습니다.

 

React를 프론트로 두고 Spring boot로 백엔드 그리고 중간 gateway Spring cloud gateway로 두고

기존 포트폴리오 프로젝트 MSA기반으로 개발로 전환 중 다시금 cors 에러를 만났습니다.

사실 단 하나의 스프링 서버를 두었을 때는 스프링 시큐리티 cofig 설정에 cors 필터를 따로 추가해주면

문제없이 서로 요청과 반환을 할 수 있었습니다.

@Configuration
public class CorsConfig {
   
// 스프링 시큐리티가 들고있는 cors 필터입니다.
    //
프론트쪽에서 계속 막힌게 이녀석 때문
   
@Bean
   
public CorsFilter corsFilter() {
       
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

       
CorsConfiguration config = new CorsConfiguration();

       
// 내 서버 데이터 응답시 json을 자바 스크립트에서 처리할수 있도록
       
config.setAllowCredentials(true);

       
// 지금 코드가 위의 setAloowCredentials 와 같이 사용되는걸 권장한다
       
config.addAllowedOriginPattern("*");
       
// 재밌는 점은 아래 코드는 이제 위의 setAllowCredentials 와 함께 사용하는걸
        //
권장하지 않는다
        //config.addAllowedOrigin("*"); //
모든 ip 응답을 허용

        //
해당 헤더를 모두 허용해줘야 프론트에서 확인받아서 체크할수있다.

        // jwt
를 담은 헤더를 리액트 쪽에서 확인할수있다는 뜻
       
config.addExposedHeader("*");

        config.
addAllowedHeader("*"); // 모든 헤더의 응답을 허용
       
config.addAllowedMethod("*"); // 모든 post,get 등등의 메소드들을 허용
       
source.registerCorsConfiguration("/**", config);
       
return new CorsFilter(source);
    }
}

이렇게 cors config를 설정해주고 (CorsFilter.java)

 

Security config 단에 corsFfilter를 추가해주면

@Bean
public SecurityFilterChain filterChain(HttpSecurity http , AuthenticationManager authenticationManager) throws Exception {
   
http
           
.authorizeRequests()
            .
antMatchers("/join/**", "/login/**", "/health/**", "/actuator/**").permitAll()
            .
anyRequest().authenticated()
            .
and()
           
// cors config 클래스로 설정을 줄꺼여서 그냥 이대로 주석처리
            //
유저 패스워드 값으로 로그인을 진행 안함 , 폼로그인 x
           
.formLogin().disable()
           
//.cors().disable()
           
.csrf().disable()
            .
addFilter(corsFilter) // @CrossOrigin (인증 x), 시큐리티 필터 등록 인증
            //
기본적인 http 로그인방식도 사용하지않는다.
           
.httpBasic().disable()
            .
addFilter(new JWTLoginFilter(authenticationManager, jwtUtil(),userModelRepository))
            .
addFilter(new JWTCheckFilter(authenticationManager,
                   
jwtUtil(),
                   
userModelRepository,
                   
jwtSuperintendRepository))
           
// session은 안하는걸로 , csrf 끄기
           
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


   
return http.build();
}

 

프론트 단과 백 단에서 올바르게 값을 주고 받을 수 있습니다.

해당 값을 true로 지정하면 아래 OriginPattern 중 모든 값을 허용한다는

config.addAllowedOrigin("*") 은 사용해서는 안됩니다.

config.setAllowCredentials(true);
저는 보통 3000포트만 허용해줍니다.

config.addAllowedOriginPattern("*");

토큰을 담아야할 Authorization 헤더와 refreshtoken 헤더를 허용해주기 위해 넣어야 합니다.
config.
addAllowedHeader("*");

Get post등의 메소드를 말합니다.

config.addAllowedMethod("*");

이 녀석 때문에 좀 고생했는데

config.addExposedHeader("*");

 

ExposeHeader를 허가 해주지 않는다면 프론트 -> 백 으로 데이터는 받을 수 있으나

 

백에서 -> 프론트로 전달된 헤더 값들은 읽을 수가 없습니다.

 

그래서 꼭 필요한 설정입니다.

 

 

또 중요한 것은 API GATEWAY또한 CORS필터 설정을 통해서

 

허가해줘야합니다.

SPRING GATEWAYConfig 설정 입니다.

 

@Configuration
public class PreFlightCorsConfiguration {

   
private static final String ALLOWED_HEADERS = "x-requested-with,authorization," +
           
"refreshtoken,Access-Control-Allow-Origin,Content-Type," +
           
"credential,X-AUTH-TOKEN,X-CSRF-TOKEN";

   
private static final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS";

   
private static final String ALLOWED_ORIGIN = "http://localhost:3000";

   
private static final String ALLOWED_CREDENTIALS = "true";

   
// 중요 해당 헤더가 없다면 axios를 통한 프론트에서 확인이 불가능함
   
private static final String EXPOSE_HEADERS = "*,Authorization,Refreshtoken," +
           
"authorization,refreshtoken";

   
private static final String MAX_AGE = "3600";


   
@Bean
   
public WebFilter corsFilter() {

       
return (ServerWebExchange ctx, WebFilterChain chain) -> {

           
ServerHttpRequest request = ctx.getRequest();

           
if (CorsUtils.isPreFlightRequest(request)) {

               
ServerHttpResponse response = ctx.getResponse();

               
HttpHeaders headers = response.getHeaders();

                headers.
add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);

                headers.
add("Access-Control-Allow-Methods", ALLOWED_METHODS);

                headers.
add("Access-Control-Max-Age", MAX_AGE);

                headers.
add("Access-Control-Allow-Headers",ALLOWED_HEADERS);

                headers.
add("Access-Control-Allow-Credentials",ALLOWED_CREDENTIALS);

               
// 프론트에서 해당 헤더를 확인할수잇게 허가해줌
               
headers.add("Access-Control-Expose-Headers", EXPOSE_HEADERS);

               
if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.
setStatusCode(HttpStatus.OK);
                   
return Mono.empty();

                }

            }

            
return chain.filter(ctx);

        };

    }


}

 

 

대게 생기는 CORS문제는 백단에서 해결가능한 문제였습니다.