CORS란..?
현재 진행중인 국비과정에서 스프링으로 진입 전 MVC패턴에 대해 공부하고 또, JSP를 이용해서 Rest-API를 생성 후 html파일에서 API를 받아서 출력해보는 연습을 하게된 적이 있다.
학원에서 과제가 주어지면 내가 우물안의 개구리인건지 정말로 평균이상으로 개발에 적성이 잘맞는것인지는 잘 모르겠지만 어쨋든 항상 남들보다 빨리 끝내는 편이었다. API생성하는 것 까지는 누구보다 빠르게 완성하여 이제 출력만 시키면되는데 자꾸 Access어쩌고하는 에러가 뜬다...
도저히 혼자서 해결할 문제는 아닌거 같아서 열심히 구글링해서 이 에러가 CORS에러라는 사실을 알아냄.
그래서 CORS란 뭐냐?
Cross Origin Resource Sharing
CORS는 한 도메인 또는 Origin의 웹 페이지가 다른 도메인 (도메인 간 요청)을 가진 리소스에 액세스 할 수 있게하는 보안 메커니즘이다.
CORS는 서버와 클라이언트가 정해진 헤더를 통해 서로 요청이나 응답에 반응할지 결정하는 방식으로 CORS라는 이름으로 표준화 되었다. CORS는 최신 브라우저에서 구현된 동일 출처 정책(same-origin policy) 때문에 등장했다고 한다.
그럼 여기서 동일 출처 정책은 또 뭐냐 ㅋㅋ...
동일 출처 정책?
동일 출처 정책은 동일한 출처의 리소스에만 접근하도록 제한하는 것이다. 여기서 출처는 프로토콜, 호스트명, 포트가 같다는 것을 의미한다.
https://naver.com:80을 예시로 들면, https는 프로토콜, naver.com은 호스트명 80은 포트다.
왜 동일한 출처에서만 접근하도록 허용하는 것일까? 모든 출처를 허용하면 어떻게 될까?
https://bank.com 이라는 도메인 사이트가 있다 이 사이트의 api 주소는 https://bank.com/api이다. 사용자가 은행 사이트에서 로그인을 한 후 인증 토큰을 받았다. 그런데 사용자가 로그인한 상태에서 https://evil.com사이트에 접속하게 되면, https://evil.com사이트에서 https://bank.com/api로 ajax 요청을 보낼 때 유저가 획득한 인증 토큰이 자동으로 첨부되어 사용자인척하면서 요청을 보낼 수 있게 된다.
이렇게 자동으로 쿠키가 첨부되기 때문에 보안상의 이유로 브라우저는 HTTP 호출을 동일한 출처로 제한했다.
왜 CORS가 생겼을까?
그럼 왜 CORS가 필요하게 됐을까?
이전에는 동일한 도메인에서 리소스를 받아왔는데, 지금은 클라이언트에서 도메인이 다른 서버에서 제공하는 API를 사용하는 일이 많아졌다.
그래서 이전처럼 동일한 도메인간의 요청만 할 수 없어졌고 CORS가 생겼다고 한다.
그래서 CORS는 어떻게 해결하는가?
클라이언트에서 해결하기
- 웹 브라우저 실행 옵션이나 플러그인을 통한 동일 출처 정책 회피하기
- 동일 출처 정책은 브라우저에서 임의로 하는 것이기 때문에 브라우저에서 동일 출처 정책을 사용하지 않으며 된다.
- jsonp 방식으로 json 데이터 가져오기
- 자바스크립트 파일이나 css 파일은 동일 출처 정책에 영향을 받지 않고 가져올 수 있다.
- 이를 이용해서 자바스크립트 파일을 가져와서 이를 json 형식으로 파싱해서 데이터를 사용할 수 있다.
이런 방법들이 있다고는 하지만 당시의 나는 어떻게든 html파일로 출력하고 플러그인을 설치하는 짓을 해서 해결하고 싶지않았다.
그래서 서버에서 해결하는 방법을 찾았고,해당 방법은 다음과 같다.
서버에서 해결하기
스프링 CORS
- @CrossOrigin 어노테이션 사용하기
메소드 레벨 및 글로벌 레벨에서 srping mvc 애플리케이션에서 spring cors를 지원하는 방법이다. sprign mvc는 @CorssOrigin 어노테이션을 제공한다. 이 어노테이션은 어노테이션이 달린 메소드 또는 타입을 교차 출처를 허용하는 것으로 표시한다. @CrossOrigin(origin="*", allowedHeaders = "*") @Controller public class MainController { @GetMapping(path = "/") public String main(Model model) { return "main"; } }
- origins
- 허용된 출처, 이 값은 pre-flight 응답과 실제 응답 모두에 access-control-allow-origin헤더에 배치된다.
- allowedHeaders
- 실제 요청 중에 사용할 수 있는 요청 헤더 목록이다. pre-flight의 응답 헤더인 access-control-allow-header에 값이 사용된다.
- allowCredential
- 브라우저가 요청과 관련된 쿠키를 포함해야 되는지 여부를 결정한다.
- 이 값이 true이면, pre-flight 응답에는 값이 true로 설정된 access-control-allow-credentials 헤더가 포함된다.
하지만 나는 아직 스프링을 제대로 배운적도 없고 현재 진행하고 있는건 JSP로 해결해야 하기에 내가 사용할 수 있는 방법이 아니었다.
- CorsFilter 사용하기
서블릿 필터 인터페이스를 이용하여 개발되었다. 웹 서버의 모든 리소스의 요청을 가로채서 Cross domain request인지 체크하여 실제 요청 페이지에 전달하기전에 적절한 CORS 정책과 해더들을 적용한다.
- Access-Control-Allow-Origin
- 도메인 간 요청을 할 수 있는 권한이 부여된 도메인을 지정한다.
- Access-Control-Allow-Credentials
- 도메인 간 요청에 credential 권한이 있는지 없는지 지정한다.
- Access-Control-Expose-Headers
- 노출하기에 안전한 헤더를 나타낸다.
- Access-Control-Max-Age
- pre-flighted 요청이 얼마만큼의 시간동안 캐시되는지
- Access-Control-Allow-Methods
- 리소스에 접근할 때 메소드가 허용되는지
- Access-Control-Allow-Headers
- 어떤 헤더 필드 네임이 실제 요청에서 사용할 수 있는지 가리킨다.
package filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
@WebFilter("/*")
public class CORS implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse)response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
resp.setHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
해당 코드를 작성하고나서야 비로소 문제가 해결되었다...
거의 한시간넘도록 구글링하며 이것저것 찾아보고 이것저것 다 시도해봐도 안되다가 필터에서 리스폰스 헤더를 추가하면 된다는 사실을 알게되고 해결하였다.
ps. 사실 이거 해결하고도 PUT이랑 DELETE에서 또 다시 CORS에러 나서 정신병걸릴뻔했다 ㅋㅋ..
다시 에러난 이유는java
resp.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
이 코드에서 Methods를 Method라고 적어놨더라.. 30분동안 개고생하다 오타하나 고치고 해결했다..
오타났는대도 불구하고 GET이랑 POST는 안되던게 됬던 터라 오타인줄 상상도 못하고 있었다..ㅠㅠ
다들 제대로 작동안하면 오타는 없는지 다시 한번 확인해보도록 하여요 !
'IT' 카테고리의 다른 글
Spring 웹 애플리케이션 마이그레이션: 파일 권한 문제 해결기 (0) | 2024.12.06 |
---|---|
팀 프로젝트 회고 [Pet Hub] (0) | 2024.06.26 |
팀 프로젝트 회고 [Smart Farm] (0) | 2024.06.26 |
MVC 패턴의 낯선 세계 (0) | 2024.04.10 |
객체지향의 5가지 원칙, SOLID에 대한 이해 (0) | 2024.04.02 |