Do Something
[Cors] Cors, addAllowOrigin 와 addAllowOriginPattern의 차이 ( + Spring Cloud Gateway에서의 Cors) 본문
[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요청을 보낼때를 의미합니다.
- HTTP 메서드가 GET, POST 말고 다른 메서드인 경우
- PUT
- DELETE
- CONNECT
- 등등...
- 사용자 정의 헤더가 요청에 포함된 경우
- 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 헤더의 문제입니다.
아까 본 그림입니다.
위처럼 설정을 하면 여기서 본 요청을 서버에 보내는것까지는 성공합니다.
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만 쓰거나, 정확하게 지정해주도록 해야겠습니다.
휴....