API 프록시에 CORS 지원 추가

현재 Apigee Edge 문서가 표시되고 있습니다.
Apigee X 문서로 이동
정보

CORS(교차 출처 리소스 공유)는 웹페이지에서 실행되는 자바스크립트 XMLHttpRequest(XHR) 호출이 출처가 다른 도메인의 리소스와 상호작용할 수 있도록 허용하는 표준 메커니즘입니다. CORS는 모든 브라우저에서 적용되는 '동일 출처 정책'에 일반적으로 구현되는 솔루션입니다. 예를 들어 브라우저에서 실행되는 자바스크립트 코드에서 Twitter API로 XHR 호출을 수행하면 호출이 실패합니다. 이는 브라우저에 페이지를 제공하는 도메인이 Twitter API를 제공하는 도메인과 동일하지 않기 때문입니다. CORS는 이 문제를 해결하기 위해 서버가 교차 출처 리소스 공유를 제공하려는 경우 '선택'을 하도록 허용하는 솔루션을 제공합니다.

동영상: 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 프록시를 만드는 것입니다. Edge는 클라이언트 (이 경우 브라우저)와 백엔드 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>

새 API 프록시에 CORS 추가 정책 연결

API 프록시를 만들 때 API 프록시에 'Add CORS' 정책을 연결하여 API 프록시에 CORS 지원을 추가할 수 있습니다. 이 정책을 추가하려면 프록시 빌드 마법사의 보안 페이지에서 Add CORS 헤더 체크박스를 선택합니다.

이 체크박스를 선택하면 다음 그림과 같이 Add CORS라는 정책이 자동으로 시스템에 추가되고 TargetEndpoint 응답 프리플로에 연결됩니다.

정책 아래 탐색기에 Add CORS 정책 추가 및 오른쪽 화면에서 TargetEndpoint 응답 프리플로에 연결

CORS 추가 정책은 AssignMessage 정책으로 구현되어 응답에 적절한 헤더를 추가합니다. 기본적으로 헤더는 리소스를 공유할 출처, 허용되는 메서드 등을 브라우저에 알립니다. 이러한 CORS 헤더에 대한 자세한 내용은 교차 출처 리소스 공유 W3C 권장사항을 참조하세요.

다음과 같이 정책을 수정해야 합니다.

  • 아래 코드 발췌문과 같이 content-typeauthorization 헤더(기본 인증 또는 OAuth2를 지원하는 데 필수)를 Access-Control-Allow-Headers 헤더에 추가합니다.
  • OAuth2 인증의 경우 RFC를 준수하지 않는 동작을 수정하는 단계를 수행해야 할 수도 있습니다.
  • 아래 발췌문과 같이 <Set>을 사용하여 <Add> 대신 CORS 헤더를 설정하는 것이 좋습니다. <Add>를 사용할 때 Access-Control-Allow-Origin 헤더가 이미 존재하는 경우 다음 오류가 발생합니다.

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    자세한 내용은 CORS 오류 : 헤더에 여러 값 '*, *'가 포함되어 있지만 하나만 허용됨을 참조하세요.

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

기존 프록시에 CORS 헤더 추가

새 Assign Message 정책을 수동으로 만들어 이전 섹션에 나열된 Add CORS 정책에 대한 코드를 새 Assign Message 정책에 복사해야 합니다. 그런 다음 정책을 API 프록시의 TargetEndpoint 응답 프리플로에 연결합니다. 헤더 값은 필요에 따라 수정할 수 있습니다. 정책을 만들고 연결하는 방법에 대한 자세한 내용은 정책이란 무엇인가요?를 참조하세요.

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/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </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 헤더가 포함된 CORS 추가 정책을 흐름에 추가하는 OptionsPreFlight 흐름이 생성됩니다.

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>
    

샘플 CORS 솔루션 사용

공유 흐름으로 구현된 샘플 CORS 솔루션은 GitHub에서 사용할 수 있습니다. 공유 흐름 번들을 환경에 가져와서 흐름 후크를 사용하거나 직접 API 프록시 흐름에 연결합니다. 자세한 내용은 샘플과 함께 제공되는 CORS-Shared-FLow README 파일을 참조하세요.