Back-end/DEV notes

CORS란? No 'Access-Control-Allow-Origin' header is present in the requested resource. 에러해결

cheersHena 2022. 8. 19. 03:46
반응형
웹 개발시 클라이언트단의 브라우저에서 외부 서버로 ajax 요청 시 자주 발생하는 실패 메세지. 

Fail to load '호출URI': No 'Access-Control-Allow-Origin' header is present in the requested resource. Origin '요청URI' is therefore not allowed access.

원인은 자바스크립트 엔진 표준스펙에 동일출처 정책(SOP:same-origin-policy) 보안 규칙 때문이다.

 

SOP(Same-origin Policy)

자바스크립트에서 XMLHttpRequest로 외부서버 접근시에는 같은 출처(Same Origin)의 페이지로만 접근이 가능도록 하여 동일한 출처(프로토콜, 호스트명, 포트) 가 모두 일치해야 데이터를 공유할 수 있다. 

*출처 Origin 란? 

출처란 URI구조에서 프로토콜, 호스트, 포트번호를 말한다.

출처: etloveguitar.tistory.com

위와 같이 프로토콜. 도메인. 포트번호 중 하나라도 불일치 하는 경우 SOP정책에 위반하게 된다. 

비교예시) 

URI 1 URI 2 SOP  why ? 
http://cheershena.tistory.com http://localhost:8080 X Host, Port 불일치 
https://cheershena.tistory.com https://cheershena.tistory.com X Protocol 불일치 
https://cheershena.tistory.com?id=hena https://cheershena.tistory.com/20 O Protocol, Host, Port 일치
https://cheershena.tistory.com:8080 https://cheershena.tistory.com:8081 X Port 불일치
 

하지만 여러 도메인 간의 통신 또는 외부 API등 호출이 많이 일어나는 대규모 웹 어플리케이션에서 이 보안정책은 골치거리이다. 

때문에 CORS(Cross-Origin Resource Sharing) 정책이 생겨났다.

받는 서버단에서 외부 요청을 허용하는 경우 ajax요청이 가능해지는 방식이다. 

에러를 해결하기 위해  CORS 를 알아보자. 

 

CORS 란? Cross Origin Request Sharing

CORS란 위의 실패메세지를 받은 경우처럼 클라이언트단의 브라우저에서 내부 서버가 아닌 외부 서버로 자원을 요청/공유 하는 것을 의미한다. 웹 브라우저에서 외부서버와 통신하는 경우 허용/거부 여부를 판별하기 위해 HTTP-header를 검증하는 것이다. 즉, CORS를 적용해서 SOP 정책에 따라 보안은 지키면서 내가 허용하는 특정(또는 모든) Origin은 사용할 수 있도록 해준다. 
CORS는 3가지 방식이 있다. 

1. Simple Request 단순요청

CORS - Simple Request

단순요청은 요청과 동시에 Cross Origin인지 확인하는데 단순요청의 조건은 다음과 같다.

1)  http method : GET , POST , HEAD

2) http header : Accept, Accetp-Language, Content-Language, Content-Type 

3) Content-Type : Application/x-www.-form-urlencoded , text/plain, multipart/form-data

위에 해당하면 단순요청을 한다.

 

2. Preflight Request 사전요청

 

 

사전요청은 단순요청과 달리 실제요청을 보내기 전에 요청하려는 경로와 같은 URL에 대해 서버에 OPTIONS메서드로 사전 요청을 먼저 보내고, 응답받는 과정을 통해 해당 요청의 메서드와 헤더에 대해 인식하고 있는지 체크한다. 즉, 거부 및 허용 여부를 확인한다. 

 

2.1 Preflight Request (사전 요청 정보) 

1) http method: OPTIONS 
2) http header : 
		Access-Control-Request-Method, //실제요청하려는 메서드
		Access-Control-Request-Headers, //실제요청 헤더 
		Origin //출처 (요청을 보내는 페이지의 도메인)

2.2 Preflight Request (사전 요청에 대한 응답 정보)  

1) response header :
		Access-Control-Allow-Origin //요청을 허용하는 출처 (* 와일드카드 설정시 모든요청 허용)
		Access-Control-Allow-Methods //요청을 허용하는 메서드 (디폴트는 GET / POST)
		Access-Control-Allow-Headers //요청을 허용하는 헤더
		Access-Control-Max-Age //Preflight 결과를 캐시에 저장하는 시간. 저장된 시간동안 사전요청 암함.
위와 같이 사전요청에 대한 응답헤더의 속성에서 요청을 허용하는 헤더 및 메서드가 확인 될 경우 실제 요청을 보내고, 확인되지 않으면 No 'Access-Control-Allow-Origin' header is present in the requested resource 에러가 나는 것이다. 
즉, 외부요청을 허용하기 위해서는 서버측에서 사전요청에 Response Header 에 위와 같은 속성을 추가해주어야 한다.
 

3. Credentialed Request 인증요청

마지막 방법은 인증관련 헤더를 포함할때 사용하는 방법으로 여기서는 스킵하도록 한다..

 

CORS를 이해했다면, 서버에서 어떻게 헤더를 셋팅하여 외부요청을 허용해주어야 하는지 알아보자. 
몇가지 방법이 존재하는데 여기서는 Java기준 Filter를 사용하는 방법을 다룬다. 

 

서버단 Filter를 사용하여 외부요청 허용하는 방법

1. CORSFilter.java 생성 

//cross domain 설정 
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.http.HttpServletResponse;

public class CORSFilter implements Filter{

@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 
	HttpServletResponse response = (HttpServletResponse) res;

 	response.setHeader("Access-Control-Allow-Origin", "*"); 
 	response.setHeader("Access-Control-Allow-Method","POST,GET,OPTIONS,DELETE, PUT"); 
 	response.setHeader("Access-Control-Max-Age","3600"); 
 	response.setHeader("Access-Control-Allow-Headers","Content-Type,x-requested-with,Authorization,Axxess-Control-Allow-Origin");

	chain.doFilter(req, res);
}

@Override 
public void destroy() {} 
}
 
2. web.xml 코드추가 
<!-- CORSFilter 설정-->
<filter>
  <filter-name>CORSFilter</filter-name>
  <filter-class>com.[클래스경로].CORSFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CORSFilter</filter-name>
  <url-pattern>/*<url-pattern> <!--모든요청에 매핑-->
</filter-mapping>
3. 서버 재기동 > CORS Filter 적용 완료 
 
허용해준 후 클라이언트에서 응답받은 Response Header.
Access-Control-Allow-Origin: * 
Access-Control-Allow-Method: POST,GET,OPTIONS, DELETE, PUT 
Access-Control-Allow-Headers: Access-Control-Allow-Origin, Contect-Type, x-requdsted-with, Authorization
Access-Control-Max-Age : 3600 

Access-Control-Allow-Origin이 * 이므로 모든 요청이 허용되기 때문에 성공적으로 요청이 된다. 

단, 테스트 시에는 요긴하게 쓰이지만 실제 운영시에는 보안문제로 특정 출처를 적어주는 것이 좋다. 

 

이렇게 서버측 작업으로 CORS를 해결하였는데, open API를 사용한다던지 외부서버 단 작업이 불가한 경우도 많다.

이럴경우에는 정석적인 해결법은 아니지만 우회하는 방법도 있다.  

JSONP방식으로 호출하기 , Proxy사용하기 등등의 방법이 있는데 해당 내용은 다음 이시간에...ㅎ
 

 

반응형