Como adicionar compatibilidade com CORS a um proxy de API

Você está vendo a documentação da Apigee X.
Veja a documentação da Apigee Edge.

O compartilhamento de recursos entre origens (CORS, na sigla em inglês) é um mecanismo padrão que permite que chamadas JavaScript XMLHttpRequest (XHR) executadas em uma página da Web interajam com recursos de domínios que não são de origem. O CORS é uma solução comumente implementada na política de mesma origem que é aplicada por todos os navegadores. Por exemplo, se você fizer uma chamada XHR para a API Twitter a partir do código JavaScript em execução no seu navegador, a chamada falhará. Isso ocorre porque o domínio que veicula a página para o navegador não é o mesmo que usa a API Twitter. O CORS oferece uma solução para esse problema permitindo que os servidores optem por permitir o compartilhamento de recursos entre origens.

Caso de uso típico para CORS

O código JQuery a seguir chama um serviço de destino fictício. Se executada de dentro do contexto de um navegador (uma página da Web), a chamada falhará devido à política de mesma origem:

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

Uma solução para esse problema é criar um proxy da API Apigee que chame a API de serviço no back-end. Lembre-se de que o Apigee fica entre o cliente (um navegador, neste caso) e a API de back-end (o serviço). Como o proxy da API é executado no servidor, não em um navegador, ele consegue chamar o serviço com sucesso. Em seguida, basta anexar cabeçalhos CORS à resposta TargetEndpoint. Contanto que o navegador seja compatível com CORS, esses cabeçalhos sinalizam para o navegador que não há problema em relaxar a política de mesma origem, permitindo que a chamada da API de origem cruzada seja bem-sucedida.

Depois que o proxy compatível com o CORS for criado, é possível chamar o URL do proxy da API em vez do serviço de back-end no código do lado do cliente. Exemplo:

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

Como anexar a política de CORS ao PreFlow de solicitação do ProxyEndpoint

Como anexar uma política de CORS a um novo proxy de API

É possível adicionar o suporte a CORS a um proxy de API anexando uma política de Adicionar CORS ao proxy de API das seguintes maneiras:

  • Ao criar a política marcando a caixa de seleção Adicionar cabeçalhos CORS na página Segurança do assistente Criar um proxy.
  • Ao adicioná-lo mais tarde na caixa de diálogo Adicionar política

Quando você adiciona a política de CORS marcando a caixa de seleção, uma política chamada Adicionar CORS é adicionada automaticamente ao sistema e anexada ao pré-fluxo de resposta TargetEndpoint, como mostra a figura a seguir:

Política &quot;Adicionar CORS&quot; adicionada ao navegador em &quot;Políticas&quot; e anexada ao pré-fluxo de resposta do TargetEndpoint no painel direito

A política Adicionar CORS adiciona os cabeçalhos apropriados à resposta. Basicamente, os cabeçalhos permitem que o navegador saiba com quais origens ele compartilhará os recursos, quais métodos ele aceita e assim por diante. Leia mais sobre esses cabeçalhos CORS na Recomendação W3C de compartilhamento de recursos de origem cruzada.

Modifique a política da seguinte maneira:

  • Adicione os cabeçalhos content-type e authorization (obrigatórios para oferecer suporte à autenticação básica ou OAuth2) ao cabeçalho Access-Control-Allow-Headers, como mostrado no trecho de código abaixo.
  • Para a autenticação OAuth2, talvez seja necessário corrigir um comportamento não compatível com 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>

Como adicionar cabeçalhos CORS a um proxy atual

Para adicionar a política de CORS a um proxy de API existente:

  1. Faça login na IU da Apigee.
  2. Selecione Develop > API Proxies na barra de navegação à esquerda.
  3. Selecione o proxy de API ao qual você quer adicionar a política de CORS.
  4. No editor do novo proxy de API, clique na guia Desenvolver:
  5. No painel lateral esquerdo do navegador, clique em Pré-fluxo em Endpoints de destino > padrão.
  6. Clique no botão +Step na parte superior, que corresponde ao campo "Request PreFlow". Isso exibirá uma lista categorizada de todas as políticas que podem ser criadas.
  7. Selecione CORS na categoria Segurança.
  8. Dê um nome, como Add CORS, e clique em Adicionar.

Como processar solicitações de pré-lançamento do CORS

Pré-lançamento do CORS refere-se ao envio de uma solicitação ao servidor para verificar se ele é compatível com o CORS. As respostas simuladas típicas incluem as origens de que o servidor aceitará solicitações CORS, uma lista de métodos HTTP compatíveis com solicitações CORS, cabeçalhos que podem ser usados como parte da solicitação de recurso e o tempo máximo de resposta de pré-lançamento armazenados em cache, entre outras. Se o serviço não indicar que é compatível com o CORS ou não quiser aceitar solicitações de origem cruzada do cliente, a política de origem cruzada do navegador será aplicada e todas as solicitações em vários domínios feitas do cliente para interagir com os recursos hospedados nesse servidor.

Normalmente, as solicitações de pré-lançamento do CORS são feitas com o método HTTP OPTIONS. Quando um servidor compatível com CORS recebe uma solicitação OPTIONS, ele retorna um conjunto de cabeçalhos CORS para o cliente que indica o nível de compatibilidade com CORS. Como resultado desse handshake, o cliente sabe o que é permitido solicitar do domínio que não é de origem.

Para mais informações, consulte a Recomendação W3C de compartilhamento de recursos entre origens (em inglês). Há também vários blogs e artigos no CORS que você pode consultar.

O Apigee não inclui uma solução de pré-lançamento do CORS pronta para uso, mas é possível implementá-la, conforme descrito nesta seção. O objetivo é que o proxy avalie uma solicitação OPTIONS em um fluxo condicional. O proxy pode enviar uma resposta apropriada de volta ao cliente.

Vamos analisar um fluxo de amostra e discutir as partes que processam a solicitação de pré-lançamento:

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

As principais partes desse ProxyEndpoint são as seguintes:

  • Uma RouteRule é criada para um destino NULL com uma condição para a solicitação OPTIONS. Não há um TargetEndpoint especificado. Se a solicitação OPTIONS for recebida e os cabeçalhos de solicitação de Origem e Access-Control-Request-Method não forem nulos, o proxy retornará imediatamente os cabeçalhos CORS em uma resposta ao cliente (ignorando o "back-end" de destino real padrão). Para detalhes sobre as condições de fluxo e a RouteRule, consulte Condições com variáveis de fluxo.
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    
  • É criado um fluxo OptionsPreFlight que adiciona uma política "Adicionar CORS" com os cabeçalhos CORS ao fluxo se uma solicitação OPTIONS for recebida e os cabeçalhos Access-Control-Request-Method não forem nulos.
     <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>