React & SpringBoot CORS 설정 방법

devdo·2021년 12월 21일
0

네트워크

목록 보기
6/7

CORS란?

교차 출처 리소스 공유(Cross-origin resource sharing)로,
외부(다른 도메인 서버)에서 온 자원소스들이 접근하는(=크로스 도메인) 체계를 말한다.

=> URL의 요소중 Protocol, Host, Port를 합친 것

페이지를 만들다보면 다른 출처의 자원(css, html, js 등)이 필요한 경우가 생긴다. 하지만 브라우저에서는 SOP로 인해 이미 다른 출처의 자원과 상호작용이 제한되어있다.

SOP(Same-Origin-Policy)

동일한 출처에 대한 정책을 의미한다.
동일한 출처에만 리소스를 공유할 수 있다.`라는 법률을 가진고 있다.
Cors와 반대 의미로 생각하면 쉽다.


이 때, CORS는 HTTP 헤더를 사용해 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 부여해준다.


왜 위험?

브라우저에서 입력한 정보가 다른 서버로 전송될 수 있기 때문!

이미 XMLHTTPRequest 객체의 보안상 제한되어 있다.


그래서 CORS

기본적으로 보안상의 이유로 브라우저는 Same-origin policy(SOP, 동일 출처 정책)을 따른다. 그렇기 때문에 XMLHttpRequestfetch API는 동일 출처 정책에 따라 동일한 출처의 리소스만을 요청할 수 있다.

하지만 웹 애플리케이션을 구현하다보면, 다른 출처에 있는 리소스를 요청하는 경우가 많습니다. 일례로, SPA(Single Page Application)로 구현된 웹 애플리케이션에서 다른 도메인을 가진 API 서버에 요청하는 경우는 매우 비일비재하다. 또한, web font을 요청하는 경우도 마찬가지다.

cross-origin 요청을 필요한 경우가 많아지고 있기 때문에, 이를 안전하게 처리할 정책이 필요했다. 그래서 CORS가 나온 것다. CORS는 cross-origin 요청을 제한적으로 허용함으로써 좀 더 안전하게 cross-origin 요청을 처리할 수 있는 정책인 것이다.


허용 방법

인증정보를 포함한 요청 (credentialed requests)

자바스크립트로 크로스 오리진 요청을 보내는 경우, 기본적으로 쿠키나 HTTP 인증 같은 자격 증명(credential)이 함께 전송되지 않는다.

서버에서 이를 허용하고 싶으면, 자격증명이 달린 header를 명시적으로 허용하겠다는 세팅이 서버에 필요하다.

자격증명을 함께 전송하는 방법

  • fetch의 경우 credentials: include
  • axios의 경우 withCredentials: true

자격 증명 정보가 담긴 요청을 서버에서 받아들이기로 동의했다면 서버는 응답에 Access-Control-Allow-Origin 헤더와 함께 Access-Control-Allow-Credentials: true 헤더를 추가해서 보낸다.

자격 증명이 함께 전송되는 요청을 보낼 땐 Access-Control-Allow-Origin에 *을 쓸 수 없습니다.


CORS 예방 방법

1) 프론트 프록시 서버 설정

  • nginx 서버 배포해서 사용

  • 웹팩 devServer 사용시 webpack proxy 사용

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:8081'
      }
    }
  }
}
// react
// package.json
"proxy": "http://localhost:8080"

2) 서버에서 설정

1) CrossOrigin 어노테이션


/*
      @CrossOrigin(origins = “허용주소:포트”,maxAge=3600)
      모든 origin 허용은 @CrossOrigin(origins="*") 설정
      maxAge로 preflight 캐시 저장기간 설정
*/

// http://localhost:8080 에서 들어오는 요청만 CORS 허용
@CrossOrigin(originPatterns = "http://localhost:8080")
@RestController
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "로그인 성공! ID: jayon, PW: 1234";
    }
}

2) SpringBoot Security @CrossOrigin 설정

SecurityConfig 
.addFilter(corsFilter)

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {

        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("*");
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        configuration.setAllowCredentials(true);
        configuration.addExposedHeader("Content-Disposition");
        configuration.setMaxAge(3000L);  // preflight

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

3) WebConfig 설정(✨)

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedHeaders("*")        // 모든 헤더를 허용
                .allowedMethods(
                        HttpMethod.GET.name(),
                        HttpMethod.POST.name(),
                        HttpMethod.PUT.name(),
                        HttpMethod.DELETE.name()

                );
    }
}
// 

or

@Configuration
public class WebConfig implements WebMvcConfigurer{

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                .allowedOriginPatterns("*") // 모든 요청을 허용
                .allowedHeaders("*")        // 모든 헤더를 허용
                .allowedMethods("*");       // 모든 메소드를 허용
    }
}

preflight란?

  • 브라우저는 cross-origin 요청을 전송하기 전에 OPTIONS 메소드로 사용해 요청을 보낸다.
  • Response로 Access-Control-Allow-Origin과 Access-Control-Allow-Methods가 넘어오는데 이는 서버에서 어떤 origin과 어떤 method를 허용하는지 브라우저에게 알려주는 역할을 한다.
  • 브라우저는 OPTIONS 요청이 반환되면 CORS 여부 및 요청메소드 사용 가능 여부를 확인하고, cross-origin(원래 보내려던 원요청) 요청을 보낸다.
    -> 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 체크하는 것이 preflight
    -> 매번 preflight를 보내지는 않고 캐시 저장 기간을 설정하여 보관할 수 있다.


참고

profile
배운 것을 기록합니다.

0개의 댓글