이 페이지는 Apigee 및 Apigee Hybrid에 적용됩니다.
Apigee Edge 문서 보기
CORS(교차 출처 리소스 공유)는 웹페이지에서 실행되는 자바스크립트 XMLHttpRequest(XHR) 호출이 출처가 다른 도메인의 리소스와 상호작용할 수 있도록 허용하는 표준 메커니즘입니다. CORS는 모든 브라우저에서 적용되는 동일 출처 정책에 일반적으로 구현되는 솔루션입니다. 예를 들어 브라우저에서 실행되는 자바스크립트 코드에서 Twitter API로 XHR 호출을 수행하면 호출이 실패합니다. 이는 브라우저에 페이지를 제공하는 도메인이 Twitter API를 제공하는 도메인과 동일하지 않기 때문입니다. CORS는 이 문제를 해결하기 위해 서버가 교차 출처 리소스 공유를 제공하려는 경우 선택을 하도록 허용하는 솔루션을 제공합니다.
CORS의 일반적인 사용 사례
다음 JQuery 코드는 가상의 대상 서비스를 호출합니다. 브라우저(웹페이지)의 컨텍스트 내에서 실행되는 경우 동일 출처 정책으로 인해 호출이 실패합니다.
<script> var url = "http://service.example.com"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This is where we end up! } }); }); }); </script>
이 문제를 해결하는 한 방법은 백엔드에서 서비스 API를 호출하는 Apigee API 프록시를 만드는 것입니다. Apigee는 클라이언트(이 경우 브라우저)와 백엔드 API(서비스) 사이에 있습니다. API 프록시는 브라우저가 아닌 서버에서 실행되므로 서비스를 성공적으로 호출할 수 있습니다. 그런 다음 CORS 헤더를 TargetEndpoint 응답에 연결하기만 하면 됩니다. 브라우저가 CORS를 지원하는 한 이러한 헤더는 동일 출처 정책을 완화해도 괜찮다는 것을 브라우저에 알림으로써 교차 출처 API 호출이 성공하도록 합니다.
CORS 지원 프록시가 생성되면 클라이언트 측 코드에서 백엔드 서비스 대신 API 프록시 URL을 호출할 수 있습니다. 예를 들면 다음과 같습니다.
<script> var url = "http://myorg-test.apigee.net/v1/example"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This time, we do not end up here! } }); }); }); </script>
ProxyEndpoint의 요청 PreFlow에 CORS 정책 연결
새 API 프록시에 Add CORS 정책 연결
CORS 추가 정책을 API 프록시에 다음 방식으로 연결하여 API 프록시에 CORS 지원을 추가할 수 있습니다.
- 정책을 만들 때 프록시 빌드 마법사의 보안 페이지에서 CORS 헤더 추가 체크박스를 선택합니다.
- 정책 추가 대화상자에서 나중에 추가합니다.
체크박스를 선택하여 CORS 정책을 추가할 때는 CORS 추가라는 정책이 시스템에 자동으로 추가되고 TargetEndpoint 요청 preflow에 연결됩니다.
CORS 추가 정책은 응답에 적절한 헤더를 추가합니다. 기본적으로 헤더는 리소스를 공유할 출처, 허용되는 메서드 등을 브라우저에 알립니다. 이러한 CORS 헤더에 대한 자세한 내용은 교차 출처 리소스 공유 W3C 권장사항을 참조하세요.
다음과 같이 정책을 수정해야 합니다.
- 아래 코드 발췌문과 같이
content-type
및authorization
헤더(기본 인증 또는 OAuth2를 지원하는 데 필수)를Access-Control-Allow-Headers
헤더에 추가합니다. - OAuth2 인증의 경우 RFC를 준수하지 않는 동작을 수정하는 단계를 수행해야 할 수도 있습니다.
<CORS continueOnError="false" enabled="true" name="add-cors"> <DisplayName>Add CORS</DisplayName> <AllowOrigins>{request.header.origin}</AllowOrigins> <AllowMethods>GET, PUT, POST, DELETE</AllowMethods> <AllowHeaders>origin, x-requested-with, accept, content-type, authorization</AllowHeaders> <ExposeHeaders>*</ExposeHeaders> <MaxAge>3628800</MaxAge> <AllowCredentials>false</AllowCredentials> <GeneratePreflightResponse>true</GeneratePreflightResponse> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> </CORS>
기존 프록시에 CORS 헤더 추가
새 프록시 편집기
기존 API 프록시에 CORS 정책을 추가하려면 다음 안내를 따르세요.
Cloud 콘솔에서 Apigee UI를 사용하는 경우: 프록시 개발 > API 프록시를 선택합니다.
기본 Apigee UI를 사용하는 경우: 개발 > API 프록시를 선택하고 프록시 창에서 프록시의 환경을 선택합니다.
- CORS 정책을 추가할 API 프록시를 선택합니다.
- 새 API 프록시에 대해 편집기에서 개발 탭을 클릭합니다.
- 왼쪽 창에서 정책 행에 있는 + 버튼을 클릭합니다.
정책 만들기 대화상자에서 정책 유형 선택 필드를 클릭하고 보안으로 스크롤하고 CORS를 선택합니다.
정책의 세부정보를 입력하고 만들기를 클릭합니다.
- 왼쪽 창의 대상 엔드포인트 > 기본값 아래에서 PreFlow를 클릭합니다.
- 왼쪽 창에서 프록시 엔드포인트 > 기본값 > PreFlow를 선택합니다.
- 비주얼 편집기 하단의 응답 창에서 PreFlow 옆에 있는 + 버튼을 클릭합니다.
- 정책 단계 추가 대화상자에서 CORS 정책을 선택합니다.
추가를 클릭하여 정책을 연결합니다.
기본 프록시 편집기
기존 API 프록시에 CORS 정책을 추가하려면 다음 안내를 따르세요.
- Apigee UI에 로그인합니다.
- 왼쪽 탐색 메뉴에서 개발 > API 프록시를 선택합니다. 지금 사용해 보기 버튼이 표시되면 클릭하여 새 개발 보기를 표시합니다.
- CORS 정책을 추가할 API 프록시를 선택합니다.
- 새 API 프록시에 대해 편집기에서 개발 탭을 클릭합니다.
- 왼쪽 탐색 창의 대상 엔드포인트 > 기본값 아래에서 PreFlow를 클릭합니다.
- 요청 PreFlow에 따라 맨 위에 있는 +단계 버튼을 클릭합니다. 그러면 만들 수 있는 모든 정책이 카테고리별 목록으로 표시됩니다.
- 보안 카테고리에서 CORS를 선택합니다.
Add CORS
와 같은 이름을 제공한 후 추가를 클릭합니다.
개발 보기가 아래에 표시됩니다.
CORS 실행 전 요청 처리
CORS 실행 전은 서버가 CORS를 지원하는지 확인하기 위해 서버에 요청을 전송하는 것을 의미합니다. 일반적인 실행 전 응답에는 서버가 CORS 요청을 수락할 출처, CORS 요청에 지원되는 HTTP 메서드 목록, 리소스 요청의 일부로 사용할 수 있는 헤더, 실행 전 응답이 캐시되는 최대 시간 등이 있습니다. 서비스가 CORS 지원을 나타내지 않거나 클라이언트 출처로부터 교차 출처 요청을 수락하지 않으면 브라우저의 교차 출처 정책이 시행되고, 해당 서버에서 호스팅되는 리소스와 상호작용하기 위해 클라이언트에서 수행된 모든 교차 도메인 요청이 실패합니다.
일반적으로 CORS 실행 전 요청은 HTTP OPTIONS 메서드를 통해 생성됩니다. CORS를 지원하는 서버가 OPTIONS 요청을 수신하면 CORS 지원 수준을 나타내는 클라이언트에 CORS 헤더 집합을 반환합니다. 이러한 핸드셰이크로 인해 클라이언트는 출처가 아닌 도메인에서 요청할 수 있는 것이 무엇인지 알게 됩니다.
실행 전에 대한 자세한 내용은 교차 출처 리소스 공유 W3C 권장사항을 참조하세요. 또한 CORS에 대해 참조할 수 있는 수많은 블로그와 문서가 있습니다.
Apigee는 즉시 사용할 수 있는 CORS 실행 전 솔루션을 포함하지 않지만 이 섹션에 설명된 대로 구현할 수 있습니다. 프록시의 목표는 조건부 흐름에서 OPTIONS 요청을 평가하는 것입니다. 그러면 프록시는 적절한 응답을 클라이언트에 다시 보낼 수 있습니다.
샘플 흐름을 살펴본 후 실행 전 요청을 처리하는 부분에 대해 살펴보겠습니다.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ProxyEndpoint name="default"> <Description/> <Flows> <Flow name="OptionsPreFlight"> <Request> <Step> <Name>add-cors</Name> </Step> </Request> <Response/> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </Flow> </Flows> <PreFlow name="PreFlow"> <Request/> <Response/> </PreFlow> <HTTPProxyConnection> <BasePath>/v1/cnc</BasePath> <VirtualHost>default</VirtualHost> <VirtualHost>secure</VirtualHost> </HTTPProxyConnection> <RouteRule name="NoRoute"> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule> <RouteRule name="default"> <TargetEndpoint>default</TargetEndpoint> </RouteRule> <PostFlow name="PostFlow"> <Request/> <Response/> </PostFlow> </ProxyEndpoint>
이 ProxyEndpoint의 주요 부분은 다음과 같습니다.
- RouteRule은 OPTIONS 요청 조건이 있는 NULL 대상에 생성됩니다. TargetEndpoint는 지정되지 않았습니다. OPTIONS 요청이 수신되고 Origin 및 Access-Control-Request-Method 요청 헤더가 null이 아닌 경우, 프록시는 클라이언트에 대한 응답으로 CORS 헤더를 즉시 반환합니다(실제 기본 '백엔드' 대상은 우회).
흐름 조건 및 RouteRule에 대한 자세한 내용은 흐름 변수가 있는 조건을 참조하세요.
<RouteRule name="NoRoute"> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule>
- OPTIONS 요청이 수신되고 Origin 및 Access-Control-Request-Method 요청 헤더가 null이 아닌 경우, CORS 헤더가 포함된 Add CORS 정책을 추가하는 OptionsPreFlight 흐름이 생성됩니다.
<Flow name="OptionsPreFlight"> <Request> <Step> <Name>add-cors</Name> </Step> </Request> <Response/> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </Flow>