티스토리 뷰
[CORS] 스프링부트 CORS / 리액트 Proxy가 우리를 힘들게 한다... FEAT. 스프링시큐리티 CORS
foodev 2023. 3. 26. 15:09스프링시큐리티와, webconifg에 두가지 설정법을 다룬다.
토이 프로젝트를 진행 중에 CORS 문제를 만나게 되어 글을 작성하게 되었다.
우리 팀은리액트(3000 포트)와 스프링부트(8080 포트)를 이용해 개발 중에 있다.
토요일 주말, 쉬고 있던 중 한 팀원으로 부터 다음과 같은 연락을 받았다.
"localhost:3000/api/find/team API에서 응답값이 나와요!"
응???
나는 순간적으로 localhost:3000은 리액트 포트인데 무슨 소리지? 하면서 당황했다.
뭔가 착각한거 아니야?라는 마음으로 포스트맨에서
토큰 값과 localhost:3000/api/team/find에 파라미터 값을 넣고 실행해 보니
정말 응답 값이 나왔다.
자 그러면 localhost:3000으로 API 요청을 했는데 정상응답값이 나오는 상황과 이유는 뭔지 알아보자.
문제상황
주어진 상황을 고려해 보면, 프런트엔드에서 백엔드로 요청하는 것이 아닌, 프론트엔드에서 자기 자신으로 요청하는 상황이다.
이렇게 되면 백엔드에서 응답을 받을 수 없으므로, 정상적인 동작이 아니며 예기치 않은 결과가 발생해야 한다.
하지만 프런트엔드에서 프런트엔드로(자기 자신 port로) 포스트맨으로 응답 값 테스트 시 정상 응답 값이 나왔다.
해당 원인은 무엇일까?
해당원인
브라우저에서 실행
API가 실행되는 것이 localhost:3000 (프런트)에서 -> localhost:3000 (프런트)로 요청해도 나오는 것은,
프런트엔드에서 해당 API의 URL을 호출하면, 해당 API URL의 코드가 브라우저에서 실행되기 때문이다.
이 경우는 CORS(Cross-Origin Resource Sharing) 정책이 적용되지 않아
프론트엔드에서 자신의 도메인으로 요청을 보내도 서버에서 처리가 가능하다고 한다.
이를 Same-Origin Policy라고 부르며, 프런트엔드와 백엔드가 같은 도메인이에서 실행되는 경우에만 적용된다.
도메인 / 포트 차이
도메인은 localhost로 같고 포트는 3000, 8080으로 다른 상태이다.
즉 이를 Same-Origin Policy
Same-Origin Policy
Same-Origin Policy는 웹 보안에서 가장 중요한 보안 정책 중 하나로, 웹 브라우저가 다른 출처(origin)로부터 가져온 리소스에 접근하는 것을 제한하는 보안 메커니즘이다.
* 출처
URL 스키마, 호스트 이름,. 포트 번호로 구성된 URL 일부를 의미한다.
만약 브라우저가 현재 열린 웹 페이지의 출처와 다른 출처에서 리소스를 가져온다면
Same-Origin Policy에 따라 해당 리소스에 접근할 수 없다.
이 보안 정책은 다른 출처에서(다른 외부 페이지) 스크립트를 사용하여 사용자 정보를 탈취하거나, CSRF와 같은 공격을 방지하기 위해 필요하다. (CSRF는 다루지 않음) 브라우저는 Same-Origin Policy를 적용하여, 웹 페이지가 리소스를 가져오는 동안 이러한 보안 위협으로부터 사용자를 보호한다.
예를 들어 http://example.co.kr이라는 출처에서 로그인 정보를 입력하고 이 정보를 백엔드 서버로 전송하는데,
이때 로그인 정보를 받아오는 페이지는 같은 출처 http://example.co.kr/login에서만 가져올 수 있다.
만약 다른 출처 http://other.co.kr에서 http://example.co.kr/login에 대한 axios나 ajax를 사용한다면
Same-Origin Policy에 의해 로그인 정보를 가져올 수 없으므로, 이를 이용한 정보 해킹이나, 공격으로부터 방지할 수 있다.
즉 CORS 정책으로 인해 발생하는 문제라고 볼 수 있다.
문제해결
CORS 문제는 프론트엔드와 백엔드 두 곳에서 해결할 수 있는데
여기서는 스프링부트에서 처리하는 방법을 다룬다.
스프링부트 처리 방법
스프링부트에서 처리하는 방법으로는
Controller마다 CORS 설정을 주어 처리하는 방법과
WebConfig를 이용해서 전역적으로 해결하는 방법이 있다.
WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://example.com",
"https://www.example.com",
"http://localhost:3000/",
"https://example.app/")
.allowedMethods("*");
}
1. Config 폴더에 WebConfig파일을 만들어 주고, WebMvcConfigurer 인터페이스를 선언
2. .addMapping은 어떤 경로의 요청인지 정의
3. .allowedOrigins는 어떤 도메인으로 부터의 요청인지 정의
- 보통 프론트엔드에서 로컬, 개발 서버에서 요청을 하므로, http://localhost:3000과 개발서버를 선언한다.
4. .allowedMethod는 DELETE, POST, GET, PUT 메소드 중 어떤 메소드를 허용할지 정의를 한다
위의 방법으로 해줬는데 CORS 문제가 지속적을 발생했다.
따라서 아래와 같이 allowCredentials(true)를 추가해줬더니 해결됐다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true);
}
}
Controller마다 선언하는 방법은 내가 생각하기에 비효율적이라고 생각하여 WebConfig만 다뤘다.
스프링시큐리티 CORS 적용
추가적으로 스프링 시큐리티를 이용하고 있다면
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/api/auth/**").permitAll()
.antMatchers(PERMIT_URL_ARRAY).permitAll()
.anyRequest().authenticated()
);
http.addFilterBefore(new JwtAuthFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
다음과 같이 cors()를 추가해줘서 시큐리티의 필터 앞단에서 CROS 적용을 허용 해줘야한다.
http.cors().and()
나의 경우는 스프링시큐리티를 적용해서 CORS 문제를 해결할 수 있었다.
'💻 개발 > DB&서버&네트워크&암호' 카테고리의 다른 글
- Total
- Today
- Yesterday