제목 : 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 GATEWAY의 Config
설정 입니다.
@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문제는 백단에서 해결가능한 문제였습니다.