Do Something

[Cors] Cors, addAllowOrigin 와 addAllowOriginPattern의 차이 ( + Spring Cloud Gateway에서의 Cors) 본문

Spring

[Cors] Cors, addAllowOrigin 와 addAllowOriginPattern의 차이 ( + Spring Cloud Gateway에서의 Cors)

뭐라도해야겠다 2023. 8. 2. 23:39

프로젝트를 하다가 Cors에러가 났습니다.

localhost에서 돌리고있는 프론트엔드 단에서 백엔드에 요청을 보내면 Cors에러가 났습니다.

Cors에러가 나는 과정은 다음과 같습니다.

저는 http://localhost:3000 에서 서비스를 테스트하고 있습니다.

http://jaehyoni:8088/project/all 이라는 링크에 post 요청을 보내려고 합니다

Postman에서의 요청은 다음과 같습니다

POST /project/all  HTTP/1.1
Host: i9a608.p.ssafy.io:8088

본 요청은 정상Postman은 브라우저가 아닙니다. Chrome이나 firefox같은 그런것들요

정상적으로 값을 받아오고, 별 문제가 없어보입니다.

하지만 브라우저에서는 헤더가 추가됩니다.

내 브라우저 까봤을 때 나오는거

Origin이라는 헤더가 추가되는데, 모든요청에 추가되는건 아니고 몇가지 중요한 요청들에 자동으로 적용됩니다.

 

1. Cors 요청

현재 사용하는 도메인과 다른 도메인에 리소스를 요청하는 경우입니다.

제가 현재 사용하는 도메인은 localhost:3000 입니다.

하지만 데이터는 http://jaehyoni:8888 에서 가져옵니다.

이렇게 요청을 한 도메인과 데이터를 주는 도메인이 다를 경우 Origin을 헤더에 포함시킵니다.

 

2. 복잡한 요청

복잡한 요청이라는건, Preflight요청을 보낼때를 의미합니다.

preflight

  1. HTTP 메서드가 GET, POST 말고 다른 메서드인 경우
    • PUT
    • DELETE
    • CONNECT
    • 등등...
  2. 사용자 정의 헤더가 요청에 포함된 경우
  • application/ x-www-form-urlencoded, multipart/form-data, text/plain 을 제외한 다른값이 있을 때
  • application/json 등등

이럴경우, 브라우저는 실제 요청을 보내기 전에 OPTIONS 메서드로 Preflight요청을 먼저 보냅니다.

이때 세팅되는 값은

  • Access-Control-Request-Method - Http메서드
  • Access-Control-Request-Headers - Http헤더
  • Origin - 요청이 시작된 웹 페이지의 출처

세가지 입니다. 

 

Preflight를 보내면 서버는 이 값을 보고 데이터를 줄지 말지 판단합니다. 

서버 또한 세가지를 봅니다.

  • Access-Control-Request-Method - 내가 허용한 메서드인지?
  • Access-Control-Request-Headers - 내가 허용한 헤더인지?
  • Origin - 요청이 시작된 웹 페이지의 출처 - 내가 허용한 웹 페이지인지

이중 Access-Control-Request-Method와 Access-Control-Request-Headers 두가지는 Preflight요청에서만 확인합니다. 

따라서 여기서 문제가 생긴다면 Preflight요청에서 실패한거니 서버 설정을 해주면 됩니다. 

 

Origin도 마찬가지입니다. 허용하는 목록에서 허용해주면 됩니다. 이런식으로요

@Configuration
public class GatewayConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

 

이러면 localhost:3000에서 요청을 보내도

모든 Origin 허용, 모든 Header허용, 모든 Method허용으로 Preflight도 허용하고, 실제 Http요청도 허용될것으로 보입니다. 

 

하지만 이것은 틀렸습니다. 

이렇게 설정을 하면 클라이언트에서 Cors에러를 발생시킵니다. (브라우저따라 다를수도있음)

왜일까요?

 

이 문제는 addAllowedOrigin 대신 addAllowOriginPattern("*")을 쓰면 해결됩니다. 

문제는 서버에서 데이터를 처리하고 반환하는데 설정해주는 Access-Control-Allow-Origin 헤더의 문제입니다.

 

preflight

 

아까 본 그림입니다.

위처럼 설정을 하면 여기서 본 요청을 서버에 보내는것까지는 성공합니다. 

200OK도 성공합니다. Preflight도 성공을 하고, 본 요청에도 성공을 해서 데이터를 반환합니다. 

 

본 요청에서 성공을 하여 데이터를 반환할 때 서버에서 클라이언트에 주는 데이터에 헤더를 붙입니다. 

  • Content-Type
  • Content-Length
  • Access-Control
    • Allow-Origin
    • Allow-Methods
    • Allow-Headers
    • Max-Age
    • Allow-Credentials
  • Set-Cookie, Expires, Location등등....

컨텐츠 타입이나 길이는 그렇다고 치고, Allow-Origin을 살펴봅시다. 

Access-Control-Allow-Origin는 이 요청이 허용된 출처에서 왔는지를 확인합니다. 

서버에서 클라이언트로 이 값을 줄 때, 이 값이 어디서 요청이 와서 보냈는지를 확인해줍니다.

 

addAllowOrigin("*")를 사용하면, 서버측에서 Access-Control-Allow-Origin에 "*"를 박아버립니다. 

이 값이 Credentials혹은 Authentication과 같이 보내지 않았다면 괜찮은데, 만약 브라우저에서 credentials/authentication과 함께 보냈다면 문제가 생깁니다. 

Allow-Origin은 저 두가지와 같이 사용할 수 없으므로 Cors문제가 발생해버립니다. 

 

인증정보를 담아서 보낼때는 반드시 Allow-Origin에는 현재의 도메인이 표시되어있어야 합니다. 

 

addAllowOriginPattern("*")은 이 문제를 해결해줍니다. 

들어오는 Origin 그대로 Allow-Origin에 넣어줍니다. 

 

그래서 Allow-Origin에는 현재의 도메인이 표시되게 되고, Cors문제는 생기지 않습니다. 

 

실제로 이것때문에 문제가 발생하였고, 진짜 왠지 모르겠어서 머리를 한참 박았습니다. 

다행이도 addAllowOrigin은 스프링부트 3점버전에서는 삭제됩니다. 

앞으로는 addAllowOriginPattern만 쓰거나, 정확하게 지정해주도록 해야겠습니다.

휴....