Implementar Gateways de API com o Ocelot

Dica

Esse conteúdo é um trecho do eBook da Arquitetura de Microsserviços do .NET para os Aplicativos .NET em Contêineres, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Importante

O aplicativos de microsserviço de referência eShopOnContainers está usando os recursos fornecidos pelo Envoy para implementar o gateway de API em vez do Ocelot já mencionado. Fizemos essa escolha de design devido ao suporte interno do Envoy ao protocolo WebSocket, exigido pelas novas comunicações entre serviços do gRPC implementadas no eShopOnContainers. No entanto, mantivemos esta seção no guia para que você possa considerar o Ocelot como um gateway de API simples, capacitado e leve, adequado para cenários de nível de produção. Além disso, a versão mais recente do Ocelot contém uma alteração interruptiva no esquema JSON. Considere o uso do Ocelot < v 16.0.0 ou use as rotas de chave em vez de ReRoutes.

Arquitetar e projetar seus Gateways de API

O diagrama de arquitetura a seguir mostra como os gateways de API são implementados com o Ocelot no eShopOnContainers.

Diagram showing the eShopOnContainers architecture.

Figura 6-28. Arquitetura do eShopOnContainers com Gateways de API

Esse diagrama mostra como todo o aplicativo é implantado em um só host do Docker ou computador de desenvolvimento com o "Docker CE for Windows" ou o "Docker for Mac". No entanto, a implantação em qualquer orquestrador seria muito semelhante, mas qualquer contêiner no diagrama poderia ser escalado horizontalmente no orquestrador.

Além disso, os ativos de infraestrutura, como bancos de dados, cache e agentes de mensagens devem ser descarregados do orquestrador e implantados em sistemas altamente disponíveis para a infraestrutura, como o Banco de Dados SQL do Azure, o Azure Cosmos DB, o Redis do Azure, o Barramento de Serviço do Azure ou qualquer solução de cluster de HA local.

Como você também pode observar no diagrama, a presença de vários gateways de API permite que várias equipes de desenvolvimento sejam autônomas (nesse caso, os recursos de marketing e os recursos de compra) ao desenvolver e implantar microsserviços, além dos próprios gateways de API relacionados.

Se você tivesse um único Gateway de API monolítico, isso significaria um único ponto a ser atualizado por várias equipes de desenvolvimento, o que poderia vincular todos os microsserviços com uma única parte do aplicativo.

Avançado bastante no design, às vezes, um Gateway de API refinado também pode ser limitado a um único microsserviço de negócios, dependendo da arquitetura escolhida. A presença dos limites do gateway de API determinados pela empresa ou pelo domínio ajuda a aprimorar o design.

Por exemplo, a granularidade refinada na camada do Gateway de API pode ser útil principalmente para aplicativos de interface do usuário compostos, mais avançados e baseados em microsserviços, porque o conceito de Gateway de API refinado é semelhante ao de serviço de composição de interface do usuário.

Apresentamos mais detalhes na seção anterior Criando uma interface do usuário composta baseada em microsserviços.

Como principal vantagem para muitos aplicativos de médio e grande porte, o uso de um produto de gateway de API personalizado costuma ser uma boa abordagem, mas não como um único agregador monolítico ou um gateway de API personalizado central exclusivo, a menos que esse gateway de API permita várias áreas de configuração independentes para as diversas equipes de desenvolvimento que criam microsserviços autônomos.

Microsserviços/contêineres de amostra a serem reencaminhados por meio dos Gateways de API

Por exemplo, o eShopOnContainers tem cerca de seis tipos de microsserviços internos que precisam ser publicados por meio dos Gateways de API, conforme mostrado na imagem a seguir.

Screenshot of the Services folder showing its subfolders.

Figura 6-29. Pastas do microsserviço na solução eShopOnContainers no Visual Studio

Sobre o serviço de Identidade, no design, ele fica fora do roteamento do Gateway de API porque é o único interesse paralelo no sistema, embora com o Ocelot também seja possível incluí-lo como parte das listas de reencaminhamento.

Todos esses serviços são implementados atualmente como serviços de API Web do ASP.NET Core, como você pode observar no código. Vamos nos concentrar em um dos microsserviços, como o código do microsserviço Catalog.

Screenshot of Solution Explorer showing Catalog.API project contents.

Figura 6-30. Microsserviço de API Web de exemplo (microsserviço Catálogo)

Você pode ver que o microsserviço Catálogo é um projeto de API Web ASP.NET Core típico com vários controladores e métodos como no código a seguir.

[HttpGet]
[Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType(typeof(CatalogItem),(int)HttpStatusCode.OK)]
public async Task<IActionResult> GetItemById(int id)
{
    if (id <= 0)
    {
        return BadRequest();
    }
    var item = await _catalogContext.CatalogItems.
                                          SingleOrDefaultAsync(ci => ci.Id == id);
    //…

    if (item != null)
    {
        return Ok(item);
    }
    return NotFound();
}

A solicitação HTTP acabará executando esse tipo de código C# acessando o banco de dados de microsserviço e qualquer ação adicional necessária.

Em relação à URL do microsserviço, quando os contêineres são implantados no computador de desenvolvimento local (host do Docker local), cada contêiner do microsserviço tem sempre uma porta interna (geralmente, a porta 80) especificada no respectivo Dockerfile, como no seguinte Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

A porta 80 mostrada no código é interna no host do Docker e, portanto, não pode ser acessada por aplicativos cliente.

Os aplicativos cliente poderão acessar apenas as portas externas (se houver) publicadas durante a implantação com docker-compose.

Essas portas externas não devem ser publicadas durante a implantação em um ambiente de produção. Por esse motivo específico o gateway de API pode ser usado para evitar a comunicação direta entre os aplicativos cliente e os microsserviços.

No entanto, durante o desenvolvimento, é necessário acessar o microsserviço/contêiner diretamente e executá-lo por meio do Swagger. É por isso que no eShopOnContainers, as portas externas são especificadas mesmo quando elas não precisam ser usadas pelo gateway de API ou por aplicativos cliente.

Veja um exemplo do arquivo docker-compose.override.yml para o microsserviço Catalog:

catalog-api:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - ASPNETCORE_URLS=http://0.0.0.0:80
    - ConnectionString=YOUR_VALUE
    - ... Other Environment Variables
  ports:
    - "5101:80"   # Important: In a production environment you should remove the external port (5101) kept here for microservice debugging purposes.
                  # The API Gateway redirects and access through the internal port (80).

Você pode ver como na configuração do docker-compose.override.yml a porta interna do contêiner de catálogo é a porta 80, mas a porta para acesso externo é a 5101. Mas essa porta não deve ser usada pelo aplicativo quando um gateway de API é usado, apenas para depurar, executar e testar somente o microsserviço Catalog.

Normalmente, a implantação não é feita com o docker-compose em um ambiente de produção porque o ambiente de implantação de produção certo para microsserviços é um orquestrador, como o Kubernetes ou o Service Fabric. Ao fazer a implantação nesses ambientes, use arquivos de configuração diferentes nos quais você não publica diretamente nenhuma porta externa para os microsserviços, mas sempre usa o proxy reverso do gateway de API.

Execute o microsserviço de catálogo no host do Docker local. Execute a solução eShopOnContainers completa por meio do Visual Studio (ele executa todos os serviços nos arquivos do docker-compose) ou inicie o microsserviço Catalog com o comando docker-compose a seguir no CMD ou no PowerShell posicionado na pasta em que o docker-compose.yml e o docker-compose.override.yml estão.

docker-compose run --service-ports catalog-api

Esse comando executa apenas o contêiner de serviço catalog.api e as dependências especificadas no docker-compose.yml. Nesse caso, o contêiner do SQL Server e o contêiner do RabbitMQ.

Depois, você poderá acessar diretamente o microsserviço Catalog e ver os métodos por meio da interface do usuário do Swagger, acessando-o diretamente por essa porta “externa”, nesse caso, http://host.docker.internal:5101/swagger:

Screenshot of Swagger UI showing the Catalog.API REST API.

Figura 6-31. Testando o microsserviço Catálogo com sua interface do usuário do Swagger

Neste ponto, você pode definir um ponto de interrupção no código C# no Visual Studio, testar o microsserviço com os métodos expostos na interface do usuário do Swagger e, por fim, limpar tudo com o comando docker-compose down.

No entanto, a comunicação de acesso direto com o microsserviço, nesse caso, pela porta 5101 externa, é exatamente o que você deseja evitar em seu aplicativo. E você pode evitar isso definindo o nível adicional de indireção do Gateway de API (o Ocelot, neste caso). Dessa forma, o aplicativo cliente não acessará o microsserviço diretamente.

Implementando Gateways de API com o Ocelot

O Ocelot é basicamente um conjunto de middleware que você pode aplicar em uma ordem específica.

O Ocelot foi projetado para funcionar apenas com o ASP.NET Core. A versão mais recente do pacote é a 18.0, que direcionada ao .NET 6 e, portanto, não é adequada para aplicativos .NET Framework.

Instale o Ocelot e suas dependências no projeto ASP.NET Core com o pacote NuGet do Ocelot, por meio do Visual Studio.

Install-Package Ocelot

No eShopOnContainers, a implementação do Gateway de API é um projeto simples de WebHost do ASP.NET Core, além disso, o middleware do Ocelot lida com todos os recursos do gateway de API, conforme é mostrado nesta imagem:

Screenshot of Solution Explorer showing Ocelot API gateway project.

Figura 6-32. Projeto base OcelotApiGw no eShopOnContainers

Este projeto de webhost do ASP.NET Core é criado com dois arquivos simples: Program.cs e Startup.cs.

O Program.cs precisa apenas criar e configurar o BuildWebHost típico do ASP.NET Core.

namespace OcelotApiGw
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            var builder = WebHost.CreateDefaultBuilder(args);

            builder.ConfigureServices(s => s.AddSingleton(builder))
                    .ConfigureAppConfiguration(
                          ic => ic.AddJsonFile(Path.Combine("configuration",
                                                            "configuration.json")))
                    .UseStartup<Startup>();
            var host = builder.Build();
            return host;
        }
    }
}

O ponto importante para Ocelot é o arquivo configuration.json que você precisa fornecer para o construtor pelo método AddJsonFile(). Esse configuration.json é onde você especifica todos os reencaminhamentos do Gateway de API, o que significa os pontos de extremidade externos com portas específicas e os pontos de extremidade internos correlacionados, geralmente usando portas diferentes.

{
    "ReRoutes": [],
    "GlobalConfiguration": {}
}

Há duas seções para a configuração. Uma matriz de ReRoutes e uma GlobalConfiguration. Os ReRoutes são os objetos que informam ao Ocelot como tratar uma solicitação upstream. A configuração global permite substituições das configurações específicas de ReRoute. Ela é útil quando não se deseja gerenciar muita configurações específicas de ReRoute.

Veja a seguir um exemplo simplificado do arquivo de configuração de ReRoute de um dos gateways de API do eShopOnContainers.

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "catalog-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/c/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

  ],
    "GlobalConfiguration": {
      "RequestIdKey": "OcRequestId",
      "AdministrationPath": "/administration"
    }
  }

A funcionalidade principal de um Gateway de API Ocelot é obter solicitações HTTP de entrada e encaminhá-las para um serviço downstream, simultaneamente à outra solicitação HTTP. O Ocelot descreve o roteamento de uma solicitação para outra como um ReRoute.

Por exemplo, vamos nos concentrar em um dos ReRoutes no configuration.json mencionado acima, a configuração do microsserviço Basket.

{
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
}

O DownstreamPathTemplate, o esquema e o DownstreamHostAndPorts compõem a URL interna do microsserviço ao qual essa solicitação será encaminhada.

A porta é a porta interna usada pelo serviço. Ao usar contêineres, é a porta especificada no dockerfile.

O Host é um nome de serviço que depende da resolução de nomes de serviço que você está usando. Quando o docker-compose é usado, os serviços de nomes são fornecidos pelo Host do Docker, que está usando os nomes de serviço fornecidos nos arquivos docker-compose. Quando é usado um orquestrador, como o Kubernetes ou o Service Fabric, esse nome deve ser resolvido pelo DNS ou pela resolução de nomes fornecida por cada orquestrador.

DownstreamHostAndPorts é uma matriz que contém o host e a porta de todos os serviços downstream para os quais você deseja encaminhar solicitações. Normalmente, essa configuração conterá apenas uma entrada, mas também é possível balancear a carga de solicitações dos serviços downstream e, para isso, o Ocelot permite que você adicione mais de uma entrada e depois selecione um balanceador de carga. Mas se você estiver usando o Azure e algum orquestrador, provavelmente, será melhor balancear a carga com a infraestrutura de nuvem e do orquestrador.

O UpstreamPathTemplate é a URL que o Ocelot usará para identificar qual DownstreamPathTemplate será usado para uma determinada solicitação do cliente. Por fim, o UpstreamHttpMethod é usado para que Ocelot possa distinguir entre diferentes solicitações (GET, POST, PUT) para a mesma URL.

Neste ponto, você poderia usar um único Gateway de API do Ocelot (WebHost do ASP.NET Core), com um ou vários arquivos configuration.json mesclados, ou armazenar a configuração em um repositório KV Consul.

Mas, conforme apresentado nas seções de arquitetura e design, se você realmente deseja usar microsserviços autônomos, talvez seja melhor dividir esse único Gateway de API monolítico em vários Gateways de API e/ou BFFs (back-end para front-end). Para isso, vamos ver como implementar essa abordagem com contêineres do Docker.

Usando uma única imagem de contêiner do Docker para executar o vários Gateway de API diferentes ou tipos de BFF contêineres

No eShopOnContainers, estamos usando uma só imagem de contêiner do Docker com o gateway de API do Ocelot, mas depois, em tempo de execução, criaremos serviços/contêineres diferentes para cada tipo de gateway de API/BFF fornecendo um arquivo configuration.json diferente, usando um volume do Docker para acessar uma pasta de computador diferente para cada serviço.

Diagram of a single Ocelot gateway Docker image for all API gateways.

Figura 6-33. Reutilizando uma única imagem do Docker do Ocelot em vários tipos de Gateway de API

No eShopOnContainers, a "imagem genérica do Docker de gateway de API" é criada com o projeto chamado 'OcelotApiGw' e a imagem de nome "eshop/ocelotapigw" que é especificada no arquivo docker-compose.yml. Em seguida, ao implantar no Docker, haverá quatro contêineres de Gateway de API criados com essa mesma imagem do Docker, conforme é mostrado na seguinte extração do arquivo docker-compose.yml.

  mobileshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  mobilemarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webmarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

Além disso, como você pode ver no arquivo docker-compose.override.yml a seguir, a única diferença entre esses contêineres de gateway de API é o arquivo de configuração do Ocelot, que é diferente para cada contêiner de serviço e é especificado em tempo de execução por meio de um volume do Docker.

mobileshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5200:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Shopping/apigw:/app/configuration

mobilemarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5201:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Marketing/apigw:/app/configuration

webshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5202:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Shopping/apigw:/app/configuration

webmarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5203:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Marketing/apigw:/app/configuration

Devido ao código anterior e, como é mostrado no Gerenciador do Visual Studio abaixo, o único arquivo necessário para definir cada Gateway de API de negócios/BFF é apenas um arquivo configuration.json, porque os quatro Gateways de API são baseados na mesma imagem do Docker.

Screenshot showing all API gateways with configuration.json files.

Figura 6-34. O único arquivo necessário para definir cada Gateway de API/BFF com o Ocelot é um arquivo de configuração

Dividindo o Gateway de API em vários Gateways de API, diferentes equipes de desenvolvimento concentrando-se em diferentes subconjuntos de microsserviços podem gerenciar seus próprios Gateways de API usando arquivos de configuração do Ocelot independentes. Além disso, ao mesmo tempo, elas podem reutilizar a mesma imagem do Docker do Ocelot.

Agora, se você executar o eShopOnContainers com os gateways de API (incluídos por padrão no VS quando uma solução eShopOnContainers-ServicesAndWebApps.sln é aberta ou quando o "docker-compose up" é executado), as rotas de exemplo a seguir serão executadas.

Por exemplo, ao visitar a URL upstream http://host.docker.internal:5202/api/v1/c/catalog/items/2/ atendida pelo Gateway de API webshoppingapigw, você obterá o mesmo resultado da URL Downstream interna http://catalog-api/api/v1/2 dentro do host do Docker, como no navegador a seguir.

Screenshot of a browser showing a response going through API gateway.

Figura 6-35. Acessando um microsserviço por meio de uma URL fornecida pelo Gateway de API

Por motivos de teste ou depuração, se você quiser acessar diretamente o contêiner do Docker do catálogo (somente no ambiente de desenvolvimento) sem passar pelo gateway de API, como 'catalog.api' é uma resolução de DNS interna do host do Docker (descoberta de serviço processada pelos nomes de serviço do docker-compose), a única maneira de acessar diretamente o contêiner será por meio da porta externa publicada em docker-compose.override.yml, que é fornecida apenas para testes de desenvolvimento, como http://host.docker.internal:5101/api/v1/Catalog/items/1 no navegador a seguir.

Screenshot of a browser showing a direct response to the Catalog.api.

Figura 6-36. Acesso direto a um microsserviço para fins de teste

Mas o aplicativo está configurado para acessar todos os microsserviços por meio dos gateways de API, não por "atalhos" de porta direta.

O padrão de agregação do Gateway em eShopOnContainers

Conforme apresentado anteriormente, uma maneira flexível de implementar a agregação de solicitações é com os serviços personalizados, por código. Você também pode implementar a agregação de solicitação com a funcionalidade de Agregação de Solicitação no Ocelot, mas talvez essa opção não seja flexível o suficiente. Portanto, a maneira selecionada de implementar a agregação em eShopOnContainers é com um serviço explícito do ASP.NET Core Web API para cada agregador.

De acordo com essa abordagem, o diagrama de composição do Gateway de API é, na realidade, um pouco mais amplo ao considerar os serviços de agregador que não são mostrados no diagrama de arquitetura global simplificado mostrado anteriormente.

No diagrama a seguir, você também poderá ver como os serviços do agregador funcionam com seus Gateways de API relacionados.

Diagram of eShopOnContainers architecture showing aggregator services.

Figura 6-37. Arquitetura de eShopOnContainers com serviços do agregador

Ampliando ainda mais, na área de negócios “Shopping” na imagem a seguir, veja que o número de troca de informações entre os aplicativos cliente e os microsserviços é reduzido ao usar os serviços de agregador nos gateways de API.

Diagram showing eShopOnContainers architecture zoom in.

Figura 6-38. Visão ampliada do agregador de serviços

Observe como a complexidade aumenta quando o diagrama mostra as possíveis solicitações provenientes dos gateways de API. Por outro lado, ao usar o padrão de agregador, veja como as setas em azul simplificam a comunicação de uma perspectiva do aplicativo cliente. Esse padrão não só ajuda a reduzir a troca de informações e a latência na comunicação, mas também aprimora significativamente a experiência do usuário em aplicativos remotos (aplicativos móveis e SPA).

O caso de uso da área de negócios "Marketing" e de microsserviços é simples, portanto, não havia necessidade de usar agregadores, mas também seria possível, se necessário.

Autenticação e autorização em Gateways de API do Ocelot

Em um gateway de API do Ocelot, você pode usar o serviço de autenticação, como um serviço do ASP.NET Core Web API usando o IdentityServer e fornecendo o token de autenticação dentro ou fora do gateway de API.

Como o eShopOnContainers está usando vários Gateways de API com limites baseados em BFF e em áreas de negócios, o serviço de identidade/autenticação é deixado de fora dos Gateways de API, conforme está realçado em amarelo no diagrama a seguir.

Diagram showing Identity microservice beneath the API gateway.

Figura 6-39. A posição do serviço de identidade em eShopOnContainers

No entanto, o Ocelot também dá suporte ao uso do microsserviço de Identidade/Autenticação dentro do limite do Gateway de API, como neste outro diagrama.

Diagram showing authentication in an Ocelot API Gateway.

Figura 6-40. Autenticação no Ocelot

Como mostra o diagrama anterior, quando o microsserviço Identity está abaixo do AG (gateway de API): 1) o AG solicita um token de autenticação do microsserviço de identidade, 2) O microsserviço de identidade retorna um token para o AG, 3 a 4) Solicitações de microsserviços do AG usando o token de autenticação. Como o aplicativo eShopOnContainers dividiu o gateway de API em vários gateways de API de BFF (back-end para front-end) e de áreas de negócios, outra opção seria criar um gateway de API adicional para interesses inter-relacionados. Essa opção seria razoável em uma arquitetura de microsserviço bem mais complexa com vários microsserviços de interesses paralelos. Como há apenas um interesse inter-relacionado no eShopOnContainers, foi decidido lidar com o serviço de segurança apenas fora do realm do gateway de API, para simplificar.

Em qualquer caso, se o aplicativo estiver protegido no nível do Gateway de API, o módulo de autenticação do Gateway de API do Ocelot será acessado primeiro quando houver uma tentativa de usar qualquer microsserviço protegido. Isso redireciona a solicitação HTTP para acessar o microsserviço de identidade ou de autenticação a fim de obter o token de acesso que permitirá acessar os serviços protegidos com o access_token.

É a maneira de proteger com autenticação qualquer serviço no nível do Gateway de API é definir o AuthenticationProviderKey em suas configurações relacionadas no configuration.json.

    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

Quando o Ocelot for executado, ele examinará o AuthenticationOptions.AuthenticationProviderKey dos ReRoutes e verificará se há um provedor de autenticação registrado com a chave especificada. Se não houver, o Ocelot não será iniciado. Se houver, o reencaminhamento usará esse provedor quando for executado.

Como o WebHost do Ocelot é configurado com o authenticationProviderKey = "IdentityApiKey", ele exigirá a autenticação sempre que esse serviço tiver alguma solicitação sem nenhum token de autenticação.

namespace OcelotApiGw
{
    public class Startup
    {
        private readonly IConfiguration _cfg;

        public Startup(IConfiguration configuration) => _cfg = configuration;

        public void ConfigureServices(IServiceCollection services)
        {
            var identityUrl = _cfg.GetValue<string>("IdentityUrl");
            var authenticationProviderKey = "IdentityApiKey";
                         //…
            services.AddAuthentication()
                .AddJwtBearer(authenticationProviderKey, x =>
                {
                    x.Authority = identityUrl;
                    x.RequireHttpsMetadata = false;
                    x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" }
                    };
                });
            //...
        }
    }
}

Em seguida, você também precisará definir a autorização com o atributo [Authorize] nos recursos a serem acessados, como os microsserviços, como no seguinte controlador do microsserviço Cesta.

namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
    [Route("api/v1/[controller]")]
    [Authorize]
    public class BasketController : Controller
    {
      //...
    }
}

Os ValidAudiences como "basket" são correlacionados à audiência definida em cada microsserviço com AddJwtBearer() no ConfigureServices() da classe Startup, como no código abaixo.

// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

var identityUrl = Configuration.GetValue<string>("IdentityUrl");

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "basket";
});

Se você tentar acessar qualquer microsserviço protegido, como o Basket, com uma URL de ReRoute baseada no gateway de API como http://host.docker.internal:5202/api/v1/b/basket/1, ocorrerá um erro 401 Não Autorizado, a menos que você forneça um token válido. Por outro lado, se uma URL de ReRoute for autenticada, o Ocelot invocará qualquer esquema downstream associado a ela (a URL interna do microsserviço).

Autorização na camada de ReRoute do Ocelot. O Ocelot dá suporte a autorização baseada em declarações avaliada após a autenticação. Defina a autorização em um nível de rota adicionando as linhas a seguir à configuração de Reencaminhamento.

"RouteClaimsRequirement": {
    "UserType": "employee"
}

Nesse exemplo, quando o middleware de autorização for chamado, o Ocelot descobrirá se o usuário tem o tipo de declaração 'UserType' no token e se o valor dessa declaração é 'employee'. Se não for, o usuário não será autorizado e a resposta será 403 Proibido.

Usando a entrada do Kubernetes e os Gateways de API do Ocelot

Ao usar o Kubernetes (como em um cluster do Serviço de Kubernetes do Azure), você geralmente unifica todas as solicitações HTTP por meio da camada de Entrada do Kubernetes com base no Nginx.

No Kubernetes, se você não usar nenhuma abordagem de ingress, os serviços e pods terão IPs que poderão ser roteados apenas pela rede de cluster.

Mas se você usar uma abordagem de entrada, haverá uma camada intermediária entre a Internet e seus serviços (incluindo seus Gateways de API), atuando como um proxy reverso.

Como uma definição, uma entrada é uma coleção de regras que permitem que conexões de entrada acessem os serviços de cluster. Uma entrada é configurada para fornecer aos serviços URLs acessadas externamente, balancear a carga do tráfego, terminação SSL e muito mais. Os usuários solicitam a entrada postando o recurso de entrada no servidor de API.

Em eShopOnContainers, ao desenvolver localmente e usar apenas o computador de desenvolvimento como o host do Docker, você não está usando nenhuma entrada, mas apenas vários Gateways de API.

No entanto, quando direcionado a um ambiente de "produção" baseado no Kubernetes, o eShopOnContainers está usando uma ingress na frente dos gateways de API. Dessa forma, os clientes ainda chamam a mesma URL base, mas as solicitações são encaminhadas para vários Gateways de API ou BFFs.

Observe que os gateways de API são front-ends ou fachadas que mostram somente os serviços, mas não os aplicativos Web que normalmente estão fora do escopo. Além disso, os Gateways de API podem ocultar determinados microsserviços internos.

A entrada, no entanto, está apenas redirecionando solicitações HTTP, mas não está tentando ocultar nenhum microsserviço ou aplicativo Web.

A arquitetura ideal é uma camada Nginx de entrada no Kubernetes na frente dos aplicativos Web além de vários Gateways de API/BFF do Ocelot, conforme é mostrado no diagrama a seguir.

A diagram showing how an ingress tier fits into the AKS environment.

Figura 6-41. A camada de entrada em eShopOnContainers, quando implantada no Kubernetes

Um ingress do Kubernetes funciona como um proxy reverso de todo o tráfego para o aplicativo, incluindo os aplicativos Web, que geralmente estão fora do escopo do gateway de API. Quando você implanta o eShopOnContainers no Kubernetes, ele expõe apenas alguns serviços ou pontos de extremidade via entrada, ou seja, basicamente, a seguinte lista de pós-fixados nas URLs:

  • / para o aplicativo Web cliente SPA
  • /webmvc para o aplicativo Web cliente MVC
  • /webstatus para o aplicativo Web cliente mostrando o status e as verificações de integridade
  • /webshoppingapigw para os processos de negócios Web BFF e de compras
  • /webmarketingapigw para os processos de negócios Web BFF e de marketing
  • /mobileshoppingapigw para os processos de negócios móveis BFF e de compras
  • /mobilemarketingapigw para os processos de negócios móveis BFF e de marketing

Na implantação no Kubernetes, cada gateway de API do Ocelot está usando um arquivo “configuration.json” diferente para cada pod que executa os gateways de API. Esses arquivos "configuration.json" são fornecidos pela montagem (originalmente com o script deploy.ps1) de um volume criado com base em um mapa de configurações do Kubernetes chamado 'ocelot'. Cada contêiner monta seu arquivo de configuração relacionado na pasta do contêiner chamada /app/configuration.

Nos arquivos de código-fonte do eShopOnContainers, os arquivos “configuration.json” originais podem ser encontrados na pasta k8s/ocelot/. Há um arquivo para cada BFF/APIGateway.

Recursos de interesses paralelos adicionais em um Gateway de API do Ocelot

Há outros recursos importantes que podem ser pesquisados e usados ao usar um Gateway de API do Ocelot, descritos nos links a seguir.