//크로스 도메인 관련 설정.
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET");
response.addHeader("Access-Control-Allow-Headers", "origin, x-requested-with");
XMLHttpRequest cannot load http://www.ozit.co.kr.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://abc.ozit.co.kr' is therefore not allowed access.
AJAX는 자원 요청시 XMLHttpRequest 를 통해서 처리되기 때문에 위와 같은 오류가 표시되었습니다.
http://abc.ozit.co.kr 도메인의 웹 서비스에서 http://www.ozit.co.kr 도메인으로 AJAX 호출을 하였으나, 접근할 수 없다는 말입니다.
XMLHttpRequest는 http://www.ozit.co.kr 을 불러올 수 없는데, 그 이유가 Access-Control-Allow-Origin 헤더가 요청된 자원(http://www.ozit.co.kr)에 존재하지 않기 때문이라는데요.
파이어폭스로 보는 경우, 좀 더 자세한 한글로 된 안내를 보실 수 있는데요.
"교차 원본 요청 차단: 동일 출처 정책으로 인해 http://www.ozit.co.kr 에 있는 원격 자원을 읽을 수 없습니다. 자원을 같은 도메인으로 이동시키거나 CORS를 활성화하여 해결할 수 있습니다." 라고 합니다.
같은 도메인을 사용한다면 당연히 문제가 해결되겠지만, 그러지 못하는 경우라면 CORS를 활성화 시키면 되겠군요.
CORS를 간단히 설명드리자면 Cross-Origin Resource Sharing 의 약자이며, 웹 페이지의 제한된 자원을 외부의 도메인에서의 요청(접근)을 허용해주는 매커니즘입니다. CORS는 브라우저와 서버간의 Cross-Origin 요청을 허용할지 안할지에 대한 여부를 안전하게 결정하도록 상호작용할 수 있는 방법을 정의합니다.
설명이 이것저것 잡다하게 많은데요, 실제 이를 해결하는 방법은 어렵지 않습니다.
크로스 도메인 요청을 허용할 웹 서버에서 크로스 도메인 이슈를 문제 삼지 않을 도메인을 지정해주면 됩니다.
들어오는 모든 요청에 대해 처리해주기 위해 Filter 를 하나 만들어야 될 것 같습니다.
필터(Filter) 이름은 SimpleCORSFilter 로 하기로 하고 SimpleCORSFilter class 를 Filter interface를 구현하여, 생성하도록 합니다.
그리고 이 필터를 웹서비스의 web.xml 에 등록하여 줍니다.
- web.xml -
<filter>
<filter-name>cors</filter-name>
<filter-class>com.company.project.util.domain.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
필터 이름은 cors이며, com.company.project.util.domain 패키지 내에 SimpleCORSFilter 클래스를 구현하였습니다.
모든 URL 패턴(/*)에 대해 필터가 적용되도록 하였습니다.
package com.company.project.util.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;
import org.springframework.stereotype.Component;
@Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Allow-Origin", "*");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
위의 필터는 web.xml 에 정의한 URL 패턴에 의해서 이 웹 서버로 오는 모든 요청이 이 메서드를 지나치게 됩니다. 이 메서드를 한 번 지나고 나면 결과를 내려받는 HTML 페이지 Header에 Access-Control 과 관련된 4개의 라인이 추가되게 됩니다.
* response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
- POST, GET, OPTIONS, DELETE 요청에 대해 허용하겠다는 의미입니다.
* response.setHeader("Access-Control-Max-Age", "3600");
- HTTP Request 요청에 앞서 Preflight Request 라는 요청이 발생되는데, 이는 해당 서버에 요청하는 메서드가 실행 가능한지(권한이 있는지) 확인을 위한 요청입니다. Preflight Request는 OPTIONS 메서드를 통해 서버에 전달됩니다. (위의 Methods 설정에서 OPTIONS 를 허용해 주었습니다.)
여기서 Access-Control-Max-Age 는 Preflight request를 캐시할 시간입니다. 단위는 초단위이며, 3,600초는 1시간입니다. Preflight Request를 웹브라우저에 캐시한다면 최소 1시간동안에는 서버에 재 요청하지 않을 것입니다.
* response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
이는 표준화된 규약은 아니지만, 보통 AJAX 호출이라는 것을 의미하기 위해 비공식적으로 사용되는 절차입니다. JQuery 또한 AJAX 요청 시, 이 헤더(x-requested-with)를 포함하는 것을 확인하실 수 있습니다. 여기서는 이 요청이 Ajax 요청임을 알려주기 위해 Header 에 x-request-width를 설정합니다. Form을 통한 요청과 Ajax 요청을 구분하기 위해 사용된 비표준 규약지만, 많은 라이브러리에서 이를 채택하여 사용하고 있습니다. (참고로 HTML5 부터는 Form 과 Ajax 요청을 구분할 수 있는 Header가 추가되었습니다.)
* response.setHeader("Access-Control-Allow-Origin", "*");
이 부분이 가장 중요한 부분입니다. * 는 모든 도메인에 대해 허용하겠다는 의미입니다. 즉 어떤 웹사이트라도 이 서버에 접근하여 AJAX 요청하여 결과를 가져갈 수 있도록 허용하겠다는 의미입니다.
만약 보안 이슈가 있어서 특정 도메인만 허용해야 한다면 * 대신 특정 도메인만을 지정할 수 있습니다.
대신
response.addHeader("Access-Control-Allow-Origin", "http://www.ozit.co.kr");
response.addHeader("Access-Control-Allow-Origin", "http://abc.ozit.co.kr");
response.addHeader("Access-Control-Allow-Origin", "http://test.ozrank.co.kr");
이렇게 쓰면
www.ozit.co.kr, abc.ozit.co.kr, test.ozrank.co.kr 이렇게 3개의 도메인에 대해서만 크로스 도메인을 허용하겠다는 의미입니다.
'java' 카테고리의 다른 글
is a has a (0) | 2015.05.15 |
---|---|
generic 타입 추론. (0) | 2015.05.12 |
ThreadLocal (0) | 2015.02.25 |
isAssignableFrom (0) | 2014.12.20 |
StringUtils (0) | 2014.12.19 |