Добавление поддержки CORS в прокси API

Вы просматриваете документацию Apigee Edge .
Перейдите в документацию Apigee
X.info

CORS (Cross-origin resource sharing) — это стандартный механизм, позволяющий вызовам JavaScript XMLHttpRequest (XHR), выполняемым на веб-странице, взаимодействовать с ресурсами из доменов, не являющихся доменами вашего источника. CORS — это широко распространенное решение проблемы « политики одного источника », которая применяется всеми браузерами. Например, если вы выполняете вызов XHR к API Twitter из кода JavaScript, работающего в вашем браузере, вызов завершится неудачей. Это происходит потому, что домен, обслуживающий страницу в вашем браузере, не совпадает с доменом, обслуживающим API Twitter. CORS решает эту проблему, позволяя серверам «добровольно» включать функцию совместного использования ресурсов между источниками, если они этого хотят.

Видео: Посмотрите короткое видео, чтобы узнать, как включить CORS для API-прокси.

Типичный сценарий использования 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 вы можете вызывать URL-адрес API-прокси вместо серверной службы в клиентском коде. Например:

<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>

Добавление политики CORS к новому API-прокси

Добавить поддержку CORS к API-прокси можно, прикрепив к нему политику "Добавить CORS" при его создании. Для этого установите флажок " Добавить заголовки CORS" на странице "Безопасность" мастера создания прокси.

При установке этого флажка в систему автоматически добавляется политика под названием «Добавить CORS», которая затем прикрепляется к предварительному потоку ответа TargetEndpoint, как показано на следующем рисунке:

Добавьте политику CORS в навигатор в разделе «Политики» и прикрепите ее к предварительному потоку ответа TargetEndpoint в правой панели.

Политика добавления CORS реализована как политика AssignMessage , которая добавляет соответствующие заголовки к ответу. По сути, заголовки сообщают браузеру, с какими источниками он будет делиться своими ресурсами, какие методы он принимает и так далее. Подробнее об этих заголовках CORS можно прочитать в рекомендации W3C по совместному использованию ресурсов между источниками (Cross-Origin Resource Sharing) .

Вам следует изменить политику следующим образом:

  • Добавьте заголовки content-type и authorization (необходимые для поддержки базовой аутентификации или OAuth2) в заголовок Access-Control-Allow-Headers , как показано в приведенном ниже фрагменте кода.
  • Для аутентификации OAuth2 может потребоваться предпринять шаги для исправления поведения, не соответствующего RFC .
  • Рекомендуется использовать <Set> для установки заголовков CORS вместо <Add> , как показано в приведенном ниже фрагменте. При использовании <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 к существующему прокси-серверу

Вам необходимо вручную создать новую политику назначения сообщений и скопировать в нее код политики добавления CORS, указанный в предыдущем разделе. Затем прикрепите политику к предварительному потоку ответа целевой конечной точки API-прокси. При необходимости вы можете изменить значения заголовков. Дополнительную информацию о создании и прикреплении политик см. в разделе «Что такое политика?» .

Обработка запросов CORS перед проверкой

Предварительная проверка CORS (CORS preflight) — это отправка запроса на сервер для проверки поддержки CORS. Типичные ответы предварительной проверки включают в себя информацию о том, с каких источников сервер будет принимать запросы CORS, список поддерживаемых методов HTTP для запросов CORS, заголовки, которые могут использоваться в запросе ресурса, максимальное время кэширования ответа предварительной проверки и другие данные. Если сервис не указывает на поддержку CORS или не желает принимать междоменные запросы от клиента, будет применена политика междоменных запросов браузера, и любые междоменные запросы, сделанные клиентом для взаимодействия с ресурсами, размещенными на этом сервере, будут отклонены.

Как правило, предварительные запросы CORS выполняются с использованием метода HTTP OPTIONS. Когда сервер, поддерживающий CORS, получает запрос OPTIONS, он возвращает клиенту набор заголовков CORS, указывающих на уровень поддержки CORS. В результате этого рукопожатия клиент знает, какие запросы ему разрешено отправлять с домена, не являющегося исходным.

Для получения дополнительной информации о предварительной проверке (preflight) обратитесь к рекомендациям W3C по обмену ресурсами между источниками (Cross-Origin Resource Sharing, CORS). Кроме того, существует множество блогов и статей о 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) для целевого объекта со значением NULL и условием для запроса OPTIONS. Обратите внимание, что TargetEndpoint не указан. Если запрос OPTIONS получен и заголовки запроса Origin и Access-Control-Request-Method не равны NULL, прокси-сервер немедленно возвращает заголовки CORS в ответе клиенту (обходя фактический целевой объект «бэкэнда» по умолчанию). Подробную информацию об условиях потока и правиле маршрутизации см. в разделе «Условия с переменными потока ».

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
  • Создается поток OptionsPreFlight, который добавляет политику Add CORS, содержащую заголовки CORS, в поток, если получен запрос OPTIONS и заголовки запроса Origin и Access-Control-Request-Method не являются пустыми.

     <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. Подробности см. в файле README CORS-Shared-FLow, прилагаемом к примеру.