Compartir a través de


Implementación de puertas de enlace de API con Ocelot

Sugerencia

Este contenido es un extracto del libro electrónico, ".NET Microservices Architecture for Containerized .NET Applications" (Arquitectura de microservicios de .NET para aplicaciones de .NET contenedorizadas), disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.

Miniatura de la portada del libro electrónico 'Arquitectura de microservicios de .NET para aplicaciones .NET contenedorizadas'.

Importante

La aplicación de microservicio de referencia eShopOnContainers usa actualmente las características proporcionadas por Envoy para implementar la puerta de enlace de API en lugar de la Ocelot a la que se hace referencia anteriormente. Hemos elegido este diseño debido a la compatibilidad integrada de Envoy con el protocolo WebSocket, requerido por las nuevas comunicaciones entre servicios gRPC implementadas en eShopOnContainers. Sin embargo, hemos conservado esta sección en la guía para que pueda considerar Ocelot como una puerta de enlace de API sencilla, capaz y ligera adecuada para escenarios de nivel de producción. Además, la versión más reciente de Ocelot contiene un cambio importante en su esquema json. Considere la posibilidad de usar Ocelot < v16.0.0 o use las rutas clave en lugar de ReRoutes.

Diseño y diseño de las puertas de enlace de API

En el diagrama de arquitectura siguiente se muestra cómo se implementaron las puertas de enlace de API con Ocelot en eShopOnContainers.

Diagrama que muestra la arquitectura de eShopOnContainers.

Figura 6-28. Arquitectura de eShopOnContainers con puertas de enlace de API

En ese diagrama se muestra cómo se implementa toda la aplicación en un único host de Docker o en un equipo de desarrollo con "Docker para Windows" o "Docker para Mac". Sin embargo, la implementación en cualquier orquestador sería similar, pero cualquier contenedor del diagrama se podría escalar horizontalmente en el orquestador.

Además, los recursos de infraestructura, como bases de datos, caché y agentes de mensajes, deben descargarse del orquestador e implementarse en sistemas de alta disponibilidad para la infraestructura, como Azure SQL Database, Azure Cosmos DB, Azure Redis, Azure Service Bus o cualquier solución de agrupación en clústeres de alta disponibilidad local.

Como también puede observar en el diagrama, tener varias puertas de enlace de API permite que varios equipos de desarrollo sean autónomos (en este caso, características de marketing frente a características de compras) al desarrollar e implementar sus microservicios más sus propias puertas de enlace de API relacionadas.

Si tuviera una única puerta de enlace de API monolítica que significaría que varios equipos de desarrollo actualizarían un único punto, lo que podría acoplar todos los microservicios con una sola parte de la aplicación.

En ocasiones, ir mucho más lejos en el diseño, una puerta de enlace de API específica también puede limitarse a un único microservicio empresarial en función de la arquitectura elegida. Tener los límites de la puerta de enlace de API dictados por la empresa o el dominio le ayudará a obtener un mejor diseño.

Por ejemplo, la granularidad fina en el nivel de puerta de enlace de API puede ser especialmente útil para aplicaciones de interfaz de usuario compuestas más avanzadas basadas en microservicios, ya que el concepto de una puerta de enlace de API específica es similar a un servicio de composición de la interfaz de usuario.

Profundizamos en más detalles en la sección anterior Creación de una interfaz de usuario compuesta basada en microservicios.

Como resultado clave, para muchas aplicaciones de tamaño mediano y grande, el uso de un producto de puerta de enlace de API personalizada suele ser un buen enfoque, pero no como un único agregador monolítico o una puerta de enlace de API personalizada única central, a menos que esa puerta de enlace de API permita varias áreas de configuración independientes para los distintos equipos de desarrollo que crean microservicios autónomos.

Microservicios o contenedores de ejemplo para volver a enrutar a través de las puertas de enlace de API

Por ejemplo, eShopOnContainers tiene alrededor de seis tipos de microservicio internos que deben publicarse a través de las puertas de enlace de API, como se muestra en la siguiente imagen.

Captura de pantalla de la carpeta Servicios que muestra sus subcarpetas.

Figura 6-29. Carpetas de microservicios en la solución eShopOnContainers en Visual Studio

Acerca del servicio de identidad, en el diseño se deja fuera del enrutamiento de la puerta de enlace de API porque es la única preocupación transversal en el sistema, aunque con Ocelot también es posible incluirlo como parte de las listas de reenrutamiento.

Todos esos servicios se implementan actualmente como ASP.NET servicios core web API, como puede indicar desde el código. Vamos a centrarnos en uno de los microservicios, como el código de microservicio de catálogo.

Captura de pantalla del Explorador de soluciones que muestra el contenido del proyecto Catalog.API.

Figura 6-30. Microservicio de API web de ejemplo (microservicio de catálogo)

Puede ver que el microservicio Catalog es un proyecto típico de API web de ASP.NET Core con varios controladores y métodos como en el código siguiente.

[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();
}

La solicitud HTTP terminará ejecutando ese tipo de código de C# que accede a la base de datos de microservicios y cualquier acción necesaria adicional.

Con respecto a la dirección URL del microservicio, cuando los contenedores se implementan en el equipo de desarrollo local (host de Docker local), cada contenedor de microservicios siempre tiene un puerto interno (normalmente el puerto 80) especificado en su dockerfile, como en el siguiente dockerfile:

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

El puerto 80 que se muestra en el código es interno dentro del host de Docker, por lo que las aplicaciones cliente no pueden acceder a él.

Las aplicaciones cliente solo pueden acceder a los puertos externos (si los hay) publicados al implementar con docker-compose.

Esos puertos externos no se deben publicar al implementar en un entorno de producción. Por este motivo específico, por qué desea usar la puerta de enlace de API para evitar la comunicación directa entre las aplicaciones cliente y los microservicios.

Sin embargo, al desarrollar, quiere acceder directamente al microservicio o contenedor y ejecutarlo a través de Swagger. Por eso, en eShopOnContainers, los puertos externos se siguen especificando incluso cuando la puerta de enlace de API o las aplicaciones cliente no las usarán.

Este es un ejemplo del docker-compose.override.yml archivo para el microservicio 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).

Puede ver cómo en la configuración de docker-compose.override.yml el puerto interno del contenedor catálogo es el puerto 80, pero el puerto para el acceso externo es 5101. Pero la aplicación no debe usar este puerto cuando se usa una puerta de enlace de API, solo para depurar, ejecutar y probar solo el microservicio catalog.

Normalmente, no se implementará con docker-compose en un entorno de producción porque el entorno de implementación de producción adecuado para microservicios es un orquestador como Kubernetes o Service Fabric. Al implementar en esos entornos, use archivos de configuración diferentes en los que no publique directamente ningún puerto externo para los microservicios, pero siempre usará el proxy inverso desde la puerta de enlace de API.

Ejecute el microservicio de catálogo en el host de Docker local. Ejecute la solución completa eShopOnContainers desde Visual Studio (ejecuta todos los servicios de los archivos docker-compose) o inicie el microservicio Catalog con el siguiente comando docker-compose en CMD o PowerShell situado en la carpeta donde se colocan y docker-compose.ymldocker-compose.override.yml .

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

Este comando solo ejecuta el contenedor del servicio catalog-api más las dependencias especificadas en el docker-compose.yml. En este caso, el contenedor de SQL Server y el contenedor RabbitMQ.

A continuación, puede acceder directamente al microservicio Catalog y ver sus métodos a través de la interfaz de usuario de Swagger que accede directamente a través de ese puerto "externo", en este caso http://host.docker.internal:5101/swagger:

Captura de pantalla de la interfaz de usuario de Swagger que muestra la API REST Catalog.API.

Figura 6-31. Probar el microservicio de catálogo con su interfaz de usuario de Swagger

En este punto, podría establecer un punto de interrupción en el código de C# en Visual Studio, probar el microservicio con los métodos expuestos en la interfaz de usuario de Swagger y, por último, limpiar todo con el docker-compose down comando .

Sin embargo, la comunicación de acceso directo al microservicio, en este caso a través del puerto externo 5101, es precisamente lo que desea evitar en la aplicación. Y puede evitarlo estableciendo el nivel adicional de direccionamiento indirecto de la puerta de enlace de API (Ocelot, en este caso). De este modo, la aplicación cliente no accederá directamente al microservicio.

Implementación de las puertas de enlace de API con Ocelot

Ocelot es básicamente un conjunto de middleware que se puede aplicar en un orden específico.

Ocelot está diseñado para trabajar solo con ASP.NET Core. La versión más reciente del paquete es 18.0, que tiene como destino .NET 6 y, por tanto, no es adecuada para las aplicaciones de .NET Framework.

Puede instalar Ocelot y sus dependencias en el proyecto de ASP.NET Core con el paquete NuGet de Ocelot, desde Visual Studio.

Install-Package Ocelot

En eShopOnContainers, su implementación de PUERTA de enlace de API es un proyecto sencillo ASP.NET Core WebHost y el middleware de Ocelot controla todas las características de la puerta de enlace de API, como se muestra en la siguiente imagen:

Captura de pantalla del Explorador de soluciones que muestra el proyecto de puerta de enlace de API de Ocelot.

Figura 6-32. El proyecto base OcelotApiGw en eShopOnContainers

Este proyecto ASP.NET Core WebHost se compila con dos archivos simples: Program.cs y Startup.cs.

El Program.cs solo tiene que crear y configurar el ASP.NET Core BuildWebHost típico.

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

El punto importante aquí para Ocelot es el configuration.json archivo que debe proporcionar al generador a través del AddJsonFile() método . Aquí configuration.json es donde se especifican todos los reroutes de puerta de enlace de API, lo que significa que los puntos de conexión externos con puertos específicos y los puntos de conexión internos correlacionados suelen usar puertos diferentes.

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

Hay dos secciones para la configuración. Matriz de ReRoutes y GlobalConfiguration. ReRoutes son los objetos que indican a Ocelot cómo tratar una solicitud ascendente. La configuración global permite invalidaciones de la configuración específica de ReRoute. Resulta útil si no quiere administrar una gran cantidad de configuraciones específicas de ReRoute.

Este es un ejemplo simplificado del archivo de configuración de ReRoute de una de las puertas de enlace de API de 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"
    }
  }

La funcionalidad principal de una puerta de enlace de API de Ocelot es tomar solicitudes HTTP entrantes y reenviarlas a un servicio de bajada, actualmente como otra solicitud HTTP. Ocelot describe el enrutamiento de una solicitud a otra como ReRoute.

Por ejemplo, vamos a centrarnos en uno de los reroutes de la configuration.json anterior, la configuración del microservicio 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": []
      }
}

Las propiedades DownstreamPathTemplate, Scheme y DownstreamHostAndPorts realizan la dirección URL interna del microservicio a la que se reenviará esta solicitud.

El puerto es el puerto interno que usa el servicio. Al usar contenedores, el puerto especificado en su dockerfile.

Host es un nombre de servicio que depende de la resolución de nombres de servicio que está usando. Al usar docker-compose, el host de Docker proporciona los nombres de los servicios, que usa los nombres de servicio proporcionados en los archivos docker-compose. Si usa un orquestador como Kubernetes o Service Fabric, ese nombre se debe resolver mediante la resolución dns o nombre proporcionada por cada orquestador.

DownstreamHostAndPorts es una matriz que contiene el host y el puerto de los servicios de bajada a los que desea reenviar las solicitudes. Normalmente, esta configuración solo contendrá una entrada, pero a veces es posible que quiera equilibrar la carga de las solicitudes a los servicios de bajada y Ocelot le permite agregar más de una entrada y, a continuación, seleccionar un equilibrador de carga. Pero si usa Azure y cualquier orquestador, probablemente sea una mejor idea equilibrar la carga con la infraestructura de la nube y del orquestador.

UpstreamPathTemplate es la dirección URL que Ocelot usará para identificar qué DownstreamPathTemplate se usará para una solicitud determinada del cliente. Por último, se usa UpstreamHttpMethod para que Ocelot pueda distinguir entre diferentes solicitudes (GET, POST, PUT) a la misma dirección URL.

En este momento, podría tener una única puerta de enlace de API de Ocelot (ASP.NET Core WebHost) mediante uno o varios archivos de configuration.json combinados o también puede almacenar la configuración en un almacén de KV de Consul.

Pero como se introdujo en las secciones de arquitectura y diseño, si realmente desea tener microservicios autónomos, podría ser mejor dividir esa puerta de enlace de API monolítica única en varias puertas de enlace de API o BFF (back-end para front-end). Para ello, veamos cómo implementar ese enfoque con contenedores de Docker.

Uso de una sola imagen de contenedor de Docker para ejecutar varios tipos de contenedor de API Gateway o BFF diferentes

En eShopOnContainers, usamos una sola imagen de contenedor de Docker con la puerta de enlace de API de Ocelot, pero, en tiempo de ejecución, creamos diferentes servicios o contenedores para cada tipo de API-Gateway/BFF proporcionando un archivo de configuration.json diferente, mediante un volumen de Docker para acceder a una carpeta de PC diferente para cada servicio.

Diagrama de una sola imagen de Docker de puerta de enlace de Ocelot para todas las puertas de enlace de API.

Figura 6-33. Reutilización de una sola imagen de Docker de Ocelot en varios tipos de puerta de enlace de API

En eShopOnContainers, se crea la "imagen genérica de Docker de puerta de enlace de API de Ocelot" con el proyecto denominado "OcelotApiGw" y el nombre de imagen "eshop/ocelotapigw" especificado en el archivo docker-compose.yml. A continuación, al implementar en Docker, habrá cuatro contenedores API-Gateway creados a partir de esa misma imagen de Docker, como se muestra en la siguiente extracción del archivo 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

Además, como puede ver en el siguiente archivo de docker-compose.override.yml, la única diferencia entre esos contenedores de puerta de enlace de API es el archivo de configuración de Ocelot, que es diferente para cada contenedor de servicios y se especifica en tiempo de ejecución a través de un volumen de 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

Debido a ese código anterior y, como se muestra en el Explorador de Visual Studio siguiente, el único archivo necesario para definir cada puerta de enlace de API o negocio específica es solo un archivo configuration.json, ya que las cuatro puertas de enlace de API se basan en la misma imagen de Docker.

Captura de pantalla que muestra todas las puertas de enlace de API con archivos configuration.json.

Figura 6-34. El único archivo necesario para definir cada puerta de enlace de API o BFF con Ocelot es un archivo de configuración.

Al dividir la puerta de enlace de API en varias puertas de enlace de API, los distintos equipos de desarrollo que se centran en distintos subconjuntos de microservicios pueden administrar sus propias puertas de enlace de API mediante archivos de configuración independientes de Ocelot. Además, al mismo tiempo pueden reutilizar la misma imagen de Docker de Ocelot.

Ahora, si ejecuta eShopOnContainers con las puertas de enlace de API (incluidas de forma predeterminada en VS al abrir eShopOnContainers-ServicesAndWebApps.sln solución o si ejecuta "docker-compose up"), se realizarán las rutas de ejemplo siguientes.

Por ejemplo, al visitar la dirección URL http://host.docker.internal:5202/api/v1/c/catalog/items/2/ ascendente que sirve la puerta de enlace de API webshoppingapigw, obtendrá el mismo resultado de la dirección URL http://catalog-api/api/v1/2 de bajada interna dentro del host de Docker, como en el explorador siguiente.

Captura de pantalla de un explorador que muestra una respuesta que pasa por la puerta de enlace de API.

Figura 6-35. Acceso a un microservicio a través de una dirección URL proporcionada por la puerta de enlace de API

Debido a los motivos de prueba o depuración, si desea acceder directamente al contenedor de Docker de catálogo (solo en el entorno de desarrollo) sin pasar por la puerta de enlace de API, ya que "catalog-api" es una resolución DNS interna para el host de Docker (detección de servicios controlada por nombres de servicio docker-compose), la única manera de acceder directamente al contenedor es a través del puerto externo publicado en la docker-compose.override.yml, que solo se proporciona para las pruebas de desarrollo, como http://host.docker.internal:5101/api/v1/Catalog/items/1 en el explorador siguiente.

Captura de pantalla de un explorador que muestra una respuesta directa a Catalog.api.

Figura 6-36. Acceso directo a un microservicio con fines de prueba

Pero la aplicación está configurada para que acceda a todos los microservicios a través de las puertas de enlace de API, no a través del puerto directo "accesos directos".

Patrón de agregación de puerta de enlace en eShopOnContainers

Como se ha introducido anteriormente, una manera flexible de implementar la agregación de solicitudes es con servicios personalizados, por código. La manera seleccionada de implementar la agregación en eShopOnContainers es con un servicio de API web principal de ASP.NET explícito para cada agregador.

Según ese enfoque, el diagrama de composición de la puerta de enlace de API se extiende un poco más al considerar los servicios agregadores que no se muestran en el diagrama de arquitectura global simplificado mostrado anteriormente.

En el diagrama siguiente, también puede ver cómo funcionan los servicios agregadores con sus puertas de enlace de API relacionadas.

Diagrama de la arquitectura de eShopOnContainers que muestra los servicios de agregador.

Figura 6-37. Arquitectura de eShopOnContainers con servicios de agregador

Al ampliar aún más, en el área empresarial "Shopping" de la siguiente imagen, puede ver que la chattiidad entre las aplicaciones cliente y los microservicios se reduce al usar los servicios agregadores en las puertas de enlace de API.

Diagrama en el que se muestra el zoom de la arquitectura de eShopOnContainers.

Figura 6-38. Acercar la visión de los servicios agregadores

Puede observar cómo cuando el diagrama muestra las posibles solicitudes procedentes de las puertas de enlace de API, puede ser compleja. Por otro lado, al usar el patrón de agregador, puede ver cómo las flechas en azul simplificarían la comunicación desde una perspectiva de la aplicación cliente. Este patrón no solo ayuda a reducir el chattiness y la latencia en la comunicación, sino que también mejora significativamente la experiencia del usuario para las aplicaciones remotas (aplicaciones móviles y SPA).

En el caso del área de negocio "Marketing" y los microservicios, es un caso de uso simple, por lo que no era necesario usar agregadores, pero también podría ser posible, si es necesario.

Autenticación y autorización en puertas de enlace de API de Ocelot

En una puerta de enlace de API de Ocelot, puede sentar el servicio de autenticación, como un servicio de API web principal de ASP.NET mediante IdentityServer que proporciona el token de autenticación, ya sea fuera o dentro de la puerta de enlace de API.

Dado que eShopOnContainers usa varias puertas de enlace de API con límites basados en BFF y áreas empresariales, el servicio Identity/Auth se deja fuera de las puertas de enlace de API, como se resalta en amarillo en el diagrama siguiente.

Diagrama que muestra el microservicio identity debajo de la puerta de enlace de API.

Figura 6-39. Posición del servicio de identidad en eShopOnContainers

Sin embargo, Ocelot también admite el microservicio Identity/Auth dentro del límite de la puerta de enlace de API, como en este otro diagrama.

Diagrama que muestra la autenticación en una puerta de enlace de API de Ocelot.

Figura 6-40. Autenticación en Ocelot

Como se muestra en el diagrama anterior, cuando el microservicio Identity está debajo de la puerta de enlace de API (AG): 1) AG solicita un token de autenticación del microservicio de identidad, 2) El microservicio de identidad devuelve el token al grupo de disponibilidad, 3-4) solicitudes de AG desde microservicios mediante el token de autenticación. Dado que la aplicación eShopOnContainers ha dividido la puerta de enlace de API en varias instancias de BFF (back-end para front-end) y puertas de enlace de API de áreas de negocio, otra opción habría sido crear una puerta de enlace de API adicional para cuestiones transversales. Esa opción sería justa en una arquitectura más compleja basada en microservicios con varios microservicios transversales. Dado que solo hay una preocupación transversal en eShopOnContainers, se decidió simplemente controlar el servicio de seguridad fuera del dominio de la puerta de enlace de API, por motivos de simplicidad.

En cualquier caso, si la aplicación está protegida en el nivel de puerta de enlace de API, el módulo de autenticación de la puerta de enlace de API de Ocelot se visita al principio al intentar usar cualquier microservicio protegido. Que redirige la solicitud HTTP para visitar el microservicio identidad o autenticación para obtener el token de acceso para que pueda visitar los servicios protegidos con el access_token.

La forma de proteger con la autenticación de cualquier servicio en el nivel de puerta de enlace de API es estableciendo AuthenticationProviderKey en su configuración relacionada en el configuration.json.

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

Cuando se ejecute Ocelot, examinará La autenticación de ReRoutesOptions.AuthenticationProviderKey y comprobará que hay un proveedor de autenticación registrado con la clave especificada. Si no lo hay, Ocelot no se iniciará. Si lo hay, ReRoute usará ese proveedor cuando se ejecute.

Dado que Ocelot WebHost está configurado con authenticationProviderKey = "IdentityApiKey", que requerirá autenticación siempre que ese servicio tenga solicitudes sin ningún token de autenticación.

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" }
                    };
                });
            //...
        }
    }
}

A continuación, también debe establecer la autorización con el atributo [Authorize] en cualquier recurso al que se acceda como los microservicios, como en el siguiente controlador de microservicios Basket.

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

Las validAudiences como "basket" se correlacionan con la audiencia definida en cada microservicio con AddJwtBearer() en configureServices() de la clase Startup, como en el código siguiente.

// 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";
});

Si intenta acceder a cualquier microservicio protegido, como el microservicio Basket con una dirección URL de ReRoute basada en la puerta de enlace de API como http://host.docker.internal:5202/api/v1/b/basket/1, obtendrá un 401 No autorizado a menos que proporcione un token válido. Por otro lado, si se autentica una dirección URL de ReRoute, Ocelot invocará cualquier esquema de bajada asociado a él (la dirección URL del microservicio interno).

Autorización en el nivel ReRoutes de Ocelot. Ocelot admite la autorización basada en notificaciones evaluada después de la autenticación. Para establecer la autorización en un nivel de ruta, agregue las siguientes líneas a la configuración de ReRoute.

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

En ese ejemplo, cuando se llama al middleware de autorización, Ocelot encontrará si el usuario tiene el tipo de notificación "UserType" en el token y si el valor de esa notificación es "employee". Si no es así, el usuario no estará autorizado y la respuesta será 403 prohibido.

Uso de entradas de Kubernetes más puertas de enlace de API de Ocelot

Cuando se usa Kubernetes (como en un clúster de Azure Kubernetes Service), normalmente se unifican todas las solicitudes HTTP a través del nivel de entrada de Kubernetes basado en Nginx.

En Kubernetes, si no usa ningún enfoque de entrada, los servicios y pods solo tienen direcciones IP enrutables por la red del clúster.

Pero si usa un enfoque de entrada, tendrá un nivel intermedio entre Internet y los servicios (incluidas las puertas de enlace de API), que actúa como proxy inverso.

Como definición, una entrada es una colección de reglas que permiten que las conexiones entrantes lleguen a los servicios de clúster. Se configura una entrada para proporcionar direcciones URL accesibles externamente a los servicios, equilibrar la carga del tráfico, la terminación SSL y mucho más. Los usuarios solicitan la entrada posTing the Ingress resource to the API server(PosTing the Ingress resource to the API server).

En eShopOnContainers, al desarrollar localmente y usar solo la máquina de desarrollo como host de Docker, no se usa ninguna entrada, sino solo las varias puertas de enlace de API.

Sin embargo, cuando el destino es un entorno de "producción" basado en Kubernetes, eShopOnContainers usa una entrada delante de las puertas de enlace de API. De este modo, los clientes siguen llamando a la misma dirección URL base, pero las solicitudes se enrutan a varias puertas de enlace de API o BFF.

Las puertas de enlace de API son front-end o fachadas que solo muestran los servicios, pero no las aplicaciones web que normalmente están fuera de su ámbito. Además, las puertas de enlace de API pueden ocultar determinados microservicios internos.

Sin embargo, la entrada solo redirige las solicitudes HTTP, pero no intenta ocultar ningún microservicio o aplicación web.

Tener un nivel nginx de entrada en Kubernetes delante de las aplicaciones web, además de las varias puertas de enlace de API de Ocelot o BFF es la arquitectura ideal, como se muestra en el diagrama siguiente.

Diagrama que muestra cómo encaja un nivel de entrada en el entorno de AKS.

Figura 6-41. Nivel de entrada en eShopOnContainers cuando se implementa en Kubernetes

Una entrada de Kubernetes actúa como proxy inverso para todo el tráfico a la aplicación, incluidas las aplicaciones web, que están fuera del ámbito de la puerta de enlace de API. Al implementar eShopOnContainers en Kubernetes, expone solo algunos servicios o puntos de conexión a través de la entrada, básicamente la siguiente lista de postfijos en las direcciones URL:

  • / para la aplicación web spa cliente
  • /webmvc para la aplicación web MVC cliente
  • /webstatus para la aplicación web cliente que muestra los estados o comprobaciones de estado
  • /webshoppingapigw para el BFF web y los procesos empresariales de compras
  • /webmarketingapigw para los procesos empresariales de marketing y BFF web
  • /mobileshoppingapigw para el BFF móvil y los procesos empresariales de compras
  • /mobilemarketingapigw para los procesos de negocio de marketing y BFF móviles

Al implementar en Kubernetes, cada puerta de enlace de API de Ocelot usa un archivo "configuration.json" diferente para cada pod que ejecuta las puertas de enlace de API. Los archivos "configuration.json" se proporcionan montando (originalmente con el script de deploy.ps1) un volumen creado en función de un mapa de configuración de Kubernetes denominado "ocelot". Cada contenedor monta su archivo de configuración relacionado en la carpeta del contenedor denominada /app/configuration.

En los archivos de código fuente de eShopOnContainers, los archivos originales "configuration.json" se pueden encontrar en la k8s/ocelot/ carpeta . Hay un archivo para cada BFF/APIGateway.

Características transversales adicionales en una puerta de enlace de API de Ocelot

Hay otras características importantes para investigar y usar, al usar una puerta de enlace de API de Ocelot, que se describe en los vínculos siguientes.