Azure API Management é mais que um proxy é mais que um gateway é o APIM
Visão geral API Management, e muitos exemplos de políticas
--
Introdução
O APIM é um Api Gateway com orquestrações, integrações com os principais produtos do Azure como Azure AD, B2C , Key vault e Application Insigthts, além de um super portal para desenvolvedores. saiba mais
Basicamente colocamos o APIM na frente das nossas APIs ou apis de terceiros , o APIM recebe as requisições passa por politicas e redireciona para o backend como um proxy reverso, com isso ganhamos uma série de recursos chamados de politicas que podem agregar resiliência, comportamentos, rastreamento e logs entre outros recursos.
Vou construir alguns exemplos de políticas do APIM , para isso vou usar um projeto simples publicado nesse endereço.
Backend
https://sample-api-users.azurewebsites.net
Swagger
https://sample-api-users.azurewebsites.net/swagger/index.html
Swagger Json (OpenAPI)
https://sample-api-users.azurewebsites.net/swagger/v1/swagger.json
O APIM tem esses endpoints
O APIM mesmo em uma rede interna vai sempre possuir um IP público utilizado para serviço de gerenciamento, isso faz parte da infraestrutura do produto e não representa qualquer vulnerabilidade de segurança, para saber mais sobre isso clique aqui
Gateway URL
https://smartsecretary-apim.azure-api.net
Developer portal URL
https://smartsecretary-apim.developer.azure-api.net
Controller principal de backend
Controller para teste da política de send-request
Importação e gerenciamento de APIS
A importação de novas APIs pode ser feita com qualquer arquivo no padrão OpenAPI, os famosos swaggers bem conhecidos em API .net são um exemplo disso, para isso use o menu New API e escolha o item OpenAPI passando o endereço do arquivo json do swagger acima;
ao importar o *.json do swagger verifique se o backend foi configurado
As Apis oferecem um menu lateral onde podemos importar para uma api existente novos endpoints utilizando diferentes arquivos do sawgger OpenAPI, ou podemos usar o mesmo arquivo swagger original da api atualizando as operações e recursos disponíveis conforme a evolução do projeto.
Abaixo podemos ver uma API chamada de Seed.Api recebendo um incremento de um outro arquivo do swagger com outros endpoints da API Sample
o resultado final mantem os endpoints existentes , e adiciona novos conforme abaixo;
além disso podemos exportar um especificação OpenAPI de uma api e importar em outra, facilitando a composição de APIs com os endpoints que nos desejamos agrupar.
Annotations
Podemos decorar nossos métodos com atributos extras que permitem passar mais informações para o arquivo json Open API gerado pelo swagger, um exemplo disso é o pacote Swashbuckle.AspNetCore.Annotations.
[HttpGet]
[SwaggerOperation(OperationId ="Papeis",Description = "Lista Papeis", Summary = "Papeis")]
public IEnumerable<Role> Get(){ return new List<Role> { new Role { Name = "Adm" } };}
Escopos
Antes de começar é importante entender o conceito de escopo do APIM, cada politica é aplicada em um escopo e pode ser herdara para escopos mais específicos por meio ta tag <base>
Os escopos existentes são;
- Produto: afeta todas as APIs do produto
- Todas as APIs: (afeta todos os recursos da API)
- API: afeta todas as operações (verbos) da API
- Operação : afeta a operação
Eu gosto muito dessa imagem, com ela podemos ter uma boa ideia de como os escopos funcionam e qual a sua relação de hierarquia.
Primeiro exemplo batendo no backend /role
Chamada original pelo swagger
Usando a aba Test
Usando Trace para entender o que aconteceu na chamada em cada etapa do fluxo do apim
Frontend>>Inbound processing>>Backend>>Outbound processing>>Frontend
é muito util
usando alguns headers podemos usar um endpoint com as informações do trace em um browser por exemplo.
fiz a demonstração pelo postman, primeiro adicionamos os headers na requisição;
Ocp-Apim-Trace : true
Ocp-Apim-Subscription-Key : seu id de assinatura
o resultado é esse Ocp-Apim-Trace-Location que pode ser acessado pelo browser;
Criado um mock
Acessando uma chave do key vault em uma política
Perceba que o código acima acessa a variável armazenada no named values ligada com key vault e a coloca em um header de response chamado CustomHeader, ou seja podemos testar via postman e ver o valor no header do response.
Nota: para que as chaves possam ser listadas conforme a imagem acima e preciso configurar as permissões de acesso na sua instancia do key vault , isso pode ser feito pelo menu Access Police da sua instancia do key vault e nele podemos configurar o nível de permissão para o APIM conforme abaixo
Fazendo um POST na controller IntrospectionController
Essa police obtém o token do objeto request
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
faz um post na API usando a política
<send-request mode="new" response-variable-name="tokenResponse" timeout="20" ignore-error="true">
armazena o response na variável tokenResponse
Trata a variável tokenResponse convertendo para uma string e armazenando na variável tokenGuid
<set-variable name="tokenGuid" value="@((string)((IResponse)context.Variables["tokenResponse"]).Body.As<JObject>()["tokenGuid"])" />
e por fim retorna em header customizado chamado custom-token
<set-header name="custom-token" exists-action="override"> <value>@((string)context.Variables["tokenGuid"])</value> </set-header>
da pra testar no postman tb e vamos ver o o resultado mudando em cada request pois a controller gera um novo guid a cada solicitação
Nota : Podemos perceber que é código C# é usado para fazer as implementações o nome desse recurso é policy expressions e podemos saber mais aqui policy expressions
Validando um JWT gerado pelo Azure AD B2C
Essa política usa um token fornecido pelo azure B2C e pode ser obtido nessa url
https://seedazb2c.b2clogin.com/seedazb2c.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_seed_sso
Podemos usar o Azure Ad
https://sts.windows.net/779811d8-4753-4c34-baeb-6b53957d52e3/.well-known/openid-configuration"
para saber mais sobre o Azure AD B2C clique aqui
ai podemos testar via postman
Vários parametros podem ser configurados que o token possa ser validado dentro do padrão open ID
Caso seja configurado um header customizado, por exemplo Token no lugar de Autorization, não devemos passar o Bearer no token, por exemplo.
<validate-jwt header-name="Token" failed-validation-httpcode="401" failed-validation-error-message="Falha">
<openid-config url="https://sts.windows.net/779811d8-4753-4c34-baeb-6b53957d52e3/.well-known/openid-configuration" />
</validate-jwt>
podemos validar mais características como audiência (clientId) e claims especificas do token nessa mesma política
<audiences>
<audience>audience string</audience>
</audiences><required-claims>
<claim name="country" match="all">
<value>Brasil</value>
</claim>
</required-claims>
Forward-request
perceba que existe uma politica default no escopo de todas as APIs responsável por entregar as requisições para o backend.
<policies>
<inbound />
<backend>
<forward-request timeout="30" />
</backend>
<outbound />
<on-error />
</policies>
essa politica é responsável por muitas confusões pois quando à deletamos o sistema ainda retorna duzentos mas não mais entrega o request no backend.
para saber mais clique Aqui
Definir o serviço de back-end com politica
A configuração do backend foi feita no momento da importação da API através da aba settings do ambiente de gerenciamento do APIM
mas podemos mudar isso utilizando a politica set-backend-service;
<set-backend-service base-url="base URL of the backend service" />
da para usar backend cadastrados no APIM para fazer essa configuração ;
<set-backend-service backend-id="WebApp_smartsecretary-api-v3" />
Os Backends podem ser cadastros aqui, os serviços de aplicação do Azure presentes na mesma assinatura já ficam automaticamente disponíveis
Podemos através do contexto da requisição definir um backend dinamicamente
Habilitando logs
O APIM captura uma série de informações sobre requisição http e https , transferência de dados, tempo de resposta e cache tudo isso pode ser visto na própria instancia do APIM através do menu Analytics no grupo Monitoring, mas também podemos integrar o APIM com o Application Insights aumentando a capacidade de captura de logs e analises.
As Apis podem ser acessada por http ou https , apesar de não recomendado o uso do HTTP, podemos configurar seu uso na tela abaixo;
jsonObject navegação
No backend
[HttpPost]
public IActionResult Post()
{return Ok(new
{
tokenGuid = Guid.NewGuid().ToString(),
attr = new {
value = "teste json object"
}
});}
No APIM
<set-variable name="tokenGuid" value="@((string)((IResponse)context.Variables["tokenResponse"]).Body.As<JObject>()["attr"]["value"])" />
Mudar o response retornando uma variável
<return-response>
<set-status code="200" reason="Success" />
<set-header name="custom-token" exists-action="override">
<value>@((string)context.Variables["tokenGuid"])</value>
</set-header>
<set-body>@((string)context.Variables["tokenGuid"])</set-body>
</return-response>
Mudar o response retornando JObject
Objeto a ser enviado no POST.
{
"niveis": [
{
"id": 3,
"dataAtualizacao": "2022-04-11T17:13:46.307+00:00"
},
{
"id": 1,
"dataAtualizacao": "2022-04-06T18:02:06.613+00:00"
}
]
}
Politica no APIM.
<policies>
<inbound>
<base />
<set-variable name="bodyIn" value="@(context.Request.Body.As<JObject>())" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-status code="200" reason="ok" />
<set-body>@{
var body = (JObject)context.Variables["bodyIn"];
var niveis = body["niveis"];
body.Add(new JProperty("nivelMaximo",niveis.OrderBy(_ => _["id"]).Select(_=>_["id"]).LastOrDefault()));
return body.ToString();
}</set-body>
</outbound>
<on-error>
<base />
</on-error>
</policies>
Tambem podemos usar o return-response, e mudar completamente o json de saída.
<return-response>
<set-body>@{
var response = context.Response.Body.As<JArray>();
return new JObject(
new JProperty("govbrLevel",response),
new JProperty("govbrLevelMax",response.OrderBy(_ => _["id"]).Select(_=>_["id"]).LastOrDefault())
).ToString();
}</set-body>
</return-response>
Validar Jwt com Certificado usando RS256 para verificar sua assinatura
Gerei um certificado auto assinado pelo IIS;
gerou um pfx
No caso do windows precisamos instalar esse software Win64openSSL, a versão que usei foi o Win64openSSL_ligth-1_1_1g.exe
usei esses comandos no openSSL para extrair as chaves públicas e privadas
Extract the key-pair
#openssl pkcs12 -in apimsc.pfx -nocerts -nodes -out sample.keyGet the Private Key from the key-pair
#openssl rsa -in sample.key -out sample_private.keyGet the Public Key from key pair
#openssl rsa -in sample.key -pubout -out sample_public.key
importei o certificado *.pfx no key vault
apontei o certificado do key vault no APIM na aba Certificates
Criei a politica de JWT com a função issuer-signing-key, e especifiquei no key certificate-id, id do certificado definido no APIM.
gerei um token com as chaves publicas e privadas usando o site Online JWT tool (dinochiesa.github.io)
{
"alg": "RS256",
"typ": "JWT"
}
{
"iss": "DinoChiesa.github.io",
"sub": "ming",
"aud": "sheniqua",
"iat": 1623442705,
"exp": 1734114894,
"roles.info": {
"name": "adm",
"type": "internal"
}
}
fiz a chamada no Postman com esse token assinado com um RS256
Obtive um resultado 200 mostrando que o apim conseguiu validar o token e permitir o acesso.
o APIM atualmente aceita o seguintes algoritmos assimétricos para assinatura HS256 e RS256. Para HS256 e também os seguintes algoritmos simétricos A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 para token criptografados, veja mais detalhes aqui
Obtendo dados das Claims e usando JsonConvert para converter uma string em um Dictionary<string,string>
{
"iss": "DinoChiesa.github.io",
"sub": "ming",
"aud": "sheniqua",
"iat": 1623442705,
"exp": 1623446555,
"roles.info": {
"name": "adm",
"type": "internal"
}
}<set-header name="sub-c" exists-action="override">
<value>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,string>>(jwt.Claims.GetValueOrDefault("roles.info", "?"));
return subJwt["name"];
}</value>
</set-header>
Obtendo dados das Claims e usando JsonConvert para converter uma string em um List<Dictionary<string,string>>
{
"iss": "DinoChiesa.github.io",
"sub": "arya",
"aud": "evander",
"iat": 1623703859,
"exp": 1623710003,
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson1"
},
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson2"
}
]
}<set-header name="sub-c" exists-action="override">
<value>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var jsonList = $"[{jwt.Claims.GetValueOrDefault("revocationUsers")}]";
var revocationUsers = JsonConvert.DeserializeObject<List<Dictionary<string,string>>>(jsonList);
return revocationUsers[0]["name"];
}</value>
</set-header>
Obtendo dados das Claims e usando JsonConvert para converter uma string em um Dictionary<string,List<Dictionary<string,string>>>
{
"iss": "DinoChiesa.github.io",
"sub": "arya",
"aud": "evander",
"iat": 1623703859,
"exp": 1623793613,
"data": {
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson1"
},
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson2"
}
]
}
}
<set-header name="sub-c" exists-action="override">
<value>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var dataJson = jwt.Claims.GetValueOrDefault("data");
var data = JsonConvert.DeserializeObject<Dictionary<string,List<Dictionary<string,string>>>>(dataJson);
return data["revocationUsers"][0]["name"];
}</value>
</set-header>
As três construções acima exploram a ideia de claims que contem um objeto no formato json como seu valor, e não um tipo primitivo, assim fazendo uso JsonConvert podemos transformar esse json que representa o objeto em qualquer tipo suportado dentro do ambiente de policy Expressions do APIM.
Por exemplo a primeira construção nos tínhamos apenas um objeto comum que pode ser representado por Dictionary<string,string> como se fosse um tipo dinâmico no C#, e na construção dois tínhamos um array de objetos então manipulei o json para poder gerar um a lista de Dictionary<string,string> e a partir disso fazer o que é preciso.
para entender o que é suportado dentro desse ambiente saiba mais em policy expressions
send-request Passando dados obtidos nas claims no body e no header
dados esse token;
{
"iss": "DinoChiesa.github.io",
"sub": "sheniqua",
"aud": "alma",
"iat": 1623416746,
"exp": 1624394229,
"aaa": 872,
"user.info": {
"type": "internal",
"userId": "1",
"businessId": "2"
},
"data": {
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson1"
},
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson2"
}
]
}
}
precisamos extrair o campos data e enviar para uma API externa usando o send-request.
o código para setar o body da politica send-request é basicamente manipulado usando a classe Dictionary.
a politica completa esta aqui;
a controller que recebe o post é essa
e o resultado no postman é esse
resumo
- Recebo as claims do Token com payload acima
- Valido o o token com um certificado digital carregado no APIM
- Realizo post no backend externo https://sample-api-users.azurewebsites.net/Replay
- Defino o header como application/json
- No body da requisição extraio as claims e monto um um objeto novo usando a classe Dictionary mando a informação serializada usando NewtonSoft.
- O resultado do post é o mesmo objeto que enviado
- Devolvo o retorno do post no header da requisição do APIM com nome de responseIntrospectionByPost
- Refaço a manipulação do payload do token acima no header de response da requisição do APIM com nome de responseIntrospectionByClaims, afim de comparar os resultados no postman
Modificar o body da requisição e mandar para o backend (Mudar valor de uma propriedade)
dado um post na controller abaixo;
com um body igual ao abaixo;
{
"data": {
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": "1111111111",
"name": "Wilson1"
}
]
}
}
temos uma politica de set-body conforme abaixo, onde vamos modificar o valor name de Wilson1 para Wilson2
resultado;
Observe que a nossa controller apena repassa o que recebe, logo sabemos que nosso objetivo de modificar o body da requisição na politica set-body de inbound do APIM foi alcançado.
Modificar o body da requisição e mandar para o backend (Compor um json com dados da requisição e do token)
dado esse payload do Post;
{
"data": {
"operation" : "trasnformation"
}
}
esse token;
{
"iss": "DinoChiesa.github.io",
"sub": "sheniqua",
"aud": "alma",
"iat": 1623416746,
"exp": 1624487896,
"aaa": 872,
"user.info": {
"type": "internal",
"userId": "1",
"businessId": "2",
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson1"
}
]
}
}
fazemos essa manipulação no set-body;
Não aterei o backend , então usei apenas o tracert para verificar se o json estava sendo passo corretamente para meu backend;
o resultado foi algo como isso;
{
"data":{
"operation":"trasnformation",
"revocationUsers":[
{
"actionDateTime":"2021-01-21T14:50:13.156Z",
"identification":1111111111,
"name":"Wilson1"
}
]
}
}
também é possível adicionar o user.info inteiro fazendo a seguinte modificação na politica ;
var jsonResult = new Dictionary<string,object>() {
{
"data" , new Dictionary<string,object>() {
{ "operation" , operation },
{ "revocationUsers",revocationUsers},
{ "user.info",subJwt},
}
}
};
Modificar o response da API
dado esse token
{
"iss": "DinoChiesa.github.io",
"sub": "sheniqua",
"aud": "alma",
"iat": 1623416746,
"exp": 1624478533,
"aaa": 872,
"user.info": {
"type": "internal",
"userId": "1",
"businessId": "2",
"revocationUsers": [
{
"actionDateTime": "2021-01-21T14:50:13.156Z",
"identification": 1111111111,
"name": "Wilson1"
}
]
}
}
Fazendo a validação do token conforme exemplo apresentado com a politica validate-jwt, setamos o token na variável valid-jwt e modificamos o response adicionando a estrutura data que será devolvida na resposta final da API.
<set-body>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,object>>(jwt.Claims.GetValueOrDefault("user.info", "?"));
var array = (JArray)subJwt["revocationUsers"];
var data = new JObject(new JProperty("revocationUsers",array));var jsonResult = new Dictionary<string,JObject>() {
{ "data" , data }
};
return JsonConvert.SerializeObject(jsonResult);}</set-body>
Essas manipulações estão sendo feitas na sessão outbound da politica.
Observe que temos no mesmo json tipos diferentes e por isso a melhor estratégia para a desrealização foi o tipo Dictionary<string,object>. Assim podemos comportar qualquer conteúdo, observe a politica set-header abaixo que que manipula apenas uma string, diferente do exemplo acima que manipula um JArray, ambos os valores estão na mesma estrutura;
<set-header name="x-type" exists-action="override">
<value>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,object>>(jwt.Claims.GetValueOrDefault("user.info", "?"));
return (string)subJwt["type"];
}</value>
</set-header>
outras manipulações;
<set-body>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,object>>(jwt.Claims.GetValueOrDefault("user.info", "?"));var newData = new JObject(
new JProperty("revocationUsers",(JArray)subJwt["revocationUsers"]),
new JProperty("authorizationUsers",(JArray)subJwt["authorizationUsers"])
);var jsonResult = new Dictionary<string,JObject>() {
{ "data" , newData }
};
return JsonConvert.SerializeObject(jsonResult);}</set-body><set-body>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,object>>(jwt.Claims.GetValueOrDefault("user.info", "?"));
var jsonResult = new Dictionary<string,Dictionary<string,object>>() {
{ "data" , subJwt }
};
return JsonConvert.SerializeObject(jsonResult);
}</set-body>
validar se uma propriedade foi enviada;
<set-header name="x-type" exists-action="override">
<value>@{
var jwt = (Jwt)context.Variables["valid-jwt"];
var subJwt = JsonConvert.DeserializeObject<Dictionary<string,object>>(jwt.Claims.GetValueOrDefault("user.info", "?"));
if (subJwt.ContainsKey("businessId"))
{
return (string)subJwt["businessId"];
}
else
{
return "";
}
}</value>
</set-header>
CORS
Abaixo temos uma chamada feita em uma aplicação angular 8, usando a classe http;
import { Http } from '@angular/http';
a chamada trata o retorno como uma promise e mostra o resultado no console do navegador.
this.http.get("https://apim-sample-main.azure-api.net/Account/Roles")
.toPromise()
.then((data: any) => {
data = data.json();
console.log("Success",data);
})
.catch((err: any) => {
console.log("Error", err);
});
Access to XMLHttpRequest at ‘https://apim-sample-main.azure-api.net/Account/Roles' from origin ‘http://localhost:4200' has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
veja documentação de CORS do APIM aqui
na api em questão https://apim-sample-main.azure-api.net/Account/Roles usamos a politica cors
Mantenha essa política logo abaixo do base, antes das demais politicas do processo de inbound.
e tudo funciona;
Retorna 405 (Method Not Allowed) para métodos (verbos) não implementados.
Por padrão ao bater em um endpoint do APIM com um verbo que não existe o APIM retorna um 404 enem bate no backend, caso seu banckend esteja preparado para retorna um 405 basta criar esta entrada no APIM que o memo passa a retornar o queo seu backend retornar.
Por exemplo nossa API de replay usada nos exemplos acima tem dois verbos implementados get e post
https://apim-sample-main.azure-api.net/post/replay
Se batermos em um PUT pelo Postman por exemplo, o resultado será um 404 (Not Found)
Posso apenas criar a entrada para o verbo e o forward-request vai encaminhar a requisição para seu backend, por exemplo;
e pronto assim temos um 405 (Method Not Allowed)
Existem dois inconvenientes nessa estratégia , ter que criar os verbos apenas para fazer o forward , e além disso o backend que antes estava sendo poupado agora passa a responder essas tentativas também.
Repassando um Token para o backend
Vamos acessar a controller SecretData, como podemos observar no código abaixo ela esta protegida pelo atributo Authorize;
Então será necessário que um token valido chegue no backend , percebam que se eu bater nessa api
https://apim-sample-main.azure-api.net/SecretProxy/SecretData
Sem nenhuma politica especial , mas passando um token valido o backend já recebe um token e retorna 200, sem o token retorna 401.
O que vou fazer é validar o token por meio do IDP do Active Directory do Azure B2C obter a váriavel da politica jwt e repassar esse token para o backend.
Cache
Existe duas forma de colocar cache no APIM , umas delas e apenas copiando a politica abaixo no seu processo de inbound logo depois do base;
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" downstream-caching-type="none">
<vary-by-header>Accept</vary-by-header>
<vary-by-header>Accept-Charset</vary-by-header>
<vary-by-header>Authorization</vary-by-header>
</cache-lookup>
e no seu processo de outboud a poliica abaixo;
<cache-store duration="20" />
essa configuração vai cacher o response do backend , com dois gatilhos de expurgo, um é temo com duração de 20 segundos, e ou outro e caso receba algun dos parametros de header diferente do da chamada que gerou o cache.
uma boa forma de testar o cache é com datas, criei esse backend para poder testar, observe a controller abaixo;
A outra forma e você controlar o cache de uma forma customizada criando uma chave, mas parecido com que variamos no Redis por exemplo.
Modernizando APIS SOAP
É muito comum trabalharmos com APIs legadas, e algumas ainda estão usando SOAP, esse protocolo é baseado em XML e é bem diferente das convenções RESTs atuais.
Mas com o APIM, podemos importar essas APIs usando wsdl, uma forma de contrato dessas APIS , e ainda podemos disponibilizar para nossos consumidores uma camada REST deixando o SOAP apenas no backend.
Encontrei uma coleção de APIs SOAP publicas que vou usar como exemplo;
Lista de Serviços
http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso
wsdl
http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?wsdl
No APIM temos na importação a opção de WSDL
Ao escolher WSDL basta passar a url acima para a tela de importação e configurar alguns parametros, parecido com o Open API
O legal e que podemos converter as APIS automaticamente para REST, e o próprio APIM já vai construir as politicas necessárias;
olha como Fica?
podemos notar que esse método ficou como POST , era normal fazer isso no SOAP , mas no REST normalmente usamos o GET para listar as coisas então , podemos mudar o verbo na aba frontend e até mudar para a chamada ser na raiz, ou mesmo botar um recurso e não uma operação por exemplo contry ou país.
além disso vamos precisar modificar a o bloco de inbound para adicionar um politica que configure o método POST apenas para a chamda de backend;
<set-method>POST</set-method>
Limites de Acesso (throttling)
- Rate Limit
Limita acesso protegendo o backend de picos de solicitação, normalmente usa um período curto de tempo , por exemplo, se dentro de um minuto um determinado usuário mandar 1000 solicitações, percebam que objetivo aqui é poupar uma alta carga que possa prejudicar o backend não só durante o pico mas deixando indisponível
<rate-limit-by-key
calls="1000"
renewal-period="60"
counter-key="@(context.Request.IpAddress)"
/>
1000 chamadas por minuto para o mesmo IP, o parâmetro counter-key, define o escopo da contagem e poderia ser o subscription-key por exemplo. E o parâmetro renewal-period define o período em segundos
2. Quotas
As Cotas são normalmente usadas para garantir que usuários não ultrapassem um determinado numero de chamada em um período maior, por exemplo dentro do mês, é um recurso normalmente utilizado para entregar somente aquilo que o usuário contratou no plano de consumo da API.
<quota-by-key
calls="1000000"
bandwidth="10000"
renewal-period="2629800"
counter-key="@(context.Request.IpAddress)"
/>
Restringe o IP com um total de 1.000.000 chamadas e 10.000 quilobytes de largura de banda por mês.
o parâmentro renewal-period define o período em segundos e o valor 2629800 corresponde a 30 dias em segundos (2629800 /60 /60 /24)
Url de health check
o path / status-0123456789abcdef é um endpoint de integridade padrão hospedado em todos os serviços de gerenciamento de API
Referencias usadas para os exemplos acima
- Como definir ou editar políticas de Gerenciamento de API do Azure | Microsoft Docs
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-access-restriction-policies#ValidateJWT
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-advanced-policies#SendRequest
- Sample API management policy — Filter on IP Address when using Application Gateway — Azure API Management | Microsoft Docs
- https://docs.microsoft.com/pt-br/azure/api-management/mock-api-responses?tabs=azure-portal
- https://docs.microsoft.com/pt-br/azure/api-management/add-api-manually
- https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-apimanagement
- Como usar valores nomeados nas políticas de Gerenciamento de API do Azure | Microsoft Docs
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-advanced-policies#forward-request
- https://www.svenmalvik.com/azure-apim-key-vault/
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-howto-api-inspector
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-policy-expressions
- Definir o serviço de back-end
- Repositorio GIT backend
- Add caching to improve performance in Azure API Management | Microsoft Docs
- Endereços IP do serviço de Gerenciamento de API do Azure | Microsoft Docs
Complementos
- Migrar APis /Policies de uma instancia do APIM para outra
- Azure API Management : Improve your Policies
- Azure/apim-landing-zone-accelerator (github.com)
Exemplos diversos
- feranto/azure-apim-lab: API Management — Hands-on Lab Workshop (github.com)
- https://docs.microsoft.com/pt-br/azure/api-management/policies/
- https://github.com/Azure/api-management-samples
Documentação de referencia para MTLS
- Cenário MTLS Externo
- Cenário MTLS Interno
- Exemplo Client Certificate .net core
- Politicas para Autenticação com Certificado Digital
- Testing client certificate authentication to Azure API Management with Postman | by Josh K | Medium
Ferramentas
Interessantes
- https://feranto.github.io/azure-apim-lab/apimanagement-4.html
- https://integration366.wordpress.com/2020/03/27/authentication-in-apim/
Limites
- https://github.com/MicrosoftDocs/azure-docs/blob/master/includes/api-management-service-limits.md
- https://docs.microsoft.com/pt-br/azure/api-management/api-management-features