Oriente.Acessos.Client 0.9.0
Oriente.Acessos.Client
Liga um app .NET ao AcessoService, o registro central de acessos da Oriente Farma.
É um data provider do Audit.NET: quem captura
url, corpo, status, exceção, IP e usuário é o Audit.WebApi — este pacote só entrega isso
ao serviço.
Você não escreve DTO, HttpClient nem filtro. Se estiver escrevendo, parou de usar o pacote.
Instalar
dotnet add package Oriente.Acessos.Client
dotnet add package Audit.WebApi.Core
Ligar
appsettings.json — a URL pode ficar aqui, a chave não:
{
"Acessos": {
"Url": "http://servicos_acessoservice:8080",
"TimeoutSegundos": 5
}
}
App no Easypanel usa o nome interno do serviço — o tráfego não sai da rede e o
AcessoService não precisa de domínio nem de DNS público. Fora do Easypanel, use o domínio
com https: o pacote recusa http quando o host tem ponto, porque aí a X-Api-Key e o
corpo ainda não redigido trafegariam em claro.
A chave vai em env var (Acessos__ApiKey) ou user-secrets. Ela identifica a plataforma:
idplataforma e plataforma saem dela, no servidor — o app não escolhe em nome de quem grava.
Program.cs, em app .NET 8 ou mais novo — MVC ou minimal API:
builder.Services.AddAcessosAudit(builder.Configuration);
...
app.UseAcessosAudit();
Pronto — duas linhas, e nenhum Map* nem controller muda. O middleware (0.8.0):
- vai logo depois do
Build(), antes doUseExceptionHandler: usuário, endpoint e status são lidos na volta da requisição, e assim a trilha grava o status que o handler devolveu; - grava todo endpoint do app e falha do servidor com ou sem endpoint. Asset estático, fallback da SPA, hub do SignalR, 404 de rota inexistente e preflight de CORS ficam de fora; health check anônimo nem sai do app;
- copia o corpo de erro (JSON ou texto, até 8 KB) para a
mensagemerro; HTML e resposta comprimida não; - cliente que desistiu grava status 499, sem stack trace;
- em
Development/Testing, semAcessos:UrleAcessos:ApiKey, desliga e avisa no log — o teste de API que sobe o host sem env var não precisa deif. Em qualquer outro ambiente lança, como antes; só uma das duas configurada lança em qualquer ambiente; - manda a falha de envio para o
ILoggerdo app.aoFalharnoAddAcessosAuditainda vence. - rota ignorada no pacote não vale para o middleware: cadastre em
Acessos__ApiKeys__<plataforma>__RotasIgnoradasno serviço antes de migrar.
App .NET 5–7 (filtro de MVC)
O middleware é net8.0. Abaixo disso, o filtro de MvcOptions:
builder.Services.AddAcessosAudit(builder.Configuration,
aoFalhar: ex => Log.Warning(ex, "Falha ao registrar acesso"));
builder.Services.AddControllers(o => o.AddAcessosAuditFilter());
ComAuditoriaDeAcessos (filtro de minimal API)
Continua existindo para quem já usa. App que liga o middleware sem tirar o filtro não grava em
dobro — o middleware pula o que o filtro já registrou —, mas o normal é trocar o grupo pelo
UseAcessosAudit e devolver os Map* ao que eram:
var api = app.MapGroup("/api").ComAuditoriaDeAcessos();
O status não sai do Response.StatusCode, e isso importa: no minimal API o resultado ainda
não foi executado quando o filtro devolve, então um Results.BadRequest() ainda vê 200. Quem
diz o status é o próprio resultado (IStatusCodeHttpResult), e é dele que a trilha lê.
O que o filtro liga, e o que não liga
- Todas as actions, sem
[AuditApi]em lugar nenhum. - Corpo da resposta: capturado sempre, mas descartado no caminho feliz — em 2xx/3xx a
mensagemerrovira nula e o corpo nem chega a ser serializado. O filtro de action lêcontext.Result, então capturar custa uma referência ao objeto, não uma cópia da resposta. - Corpo da requisição: desligado por padrão. Ligue por rota, com
AddAcessosAuditFilter(incluirCorpoDaRequisicao: true)ou[AuditApi(IncludeRequestBody = true)]no controller onde o corpo realmente responde "o que o usuário fez".
De qual tela do front veio a chamada
Uma tela de SPA carrega e dispara dezenas de requisições. Sem saber a tela, a trilha responde "houve 20 requisições" em vez de "o usuário abriu a tela de pedidos" — e cresce vinte vezes mais rápido do que precisa.
O front manda o header X-Pagina em toda requisição (um interceptor no Angular ou no
axios). O pacote lê e manda junto; o serviço grava na coluna pagina e agrupa por tela: a
leitura repetida do mesmo usuário na mesma tela, dentro de 30 segundos, vira uma linha. Falha,
escrita, leitura de registro identificado (/pacientes/4821) e anônimo nunca agrupam.
Desde o 0.9.0 o agrupamento mora só no serviço (ADR 0008): regra no pacote só mudava com
versão nova e redeploy de cada app. JanelaDedupeSegundos e PostDeLeituraPorConvencao
continuam compilando, marcadas [Obsolete], e não fazem nada; a janela é
Acessos__JanelaAgrupamentoSegundos do AcessoService.
Sem o header o pacote cai no Referer, e só se ele for do mesmo host da requisição —
Referer de outra origem é descartado, porque o host é jogado fora na normalização e
https://evil.com/pedidos ficaria idêntico a /pedidos do seu front. A coluna fica nula e o
agrupamento do serviço passa a valer por url.
Libere X-Pagina no CORS da API (WithHeaders("X-Pagina", ...)). Header custom fora da
policy faz o browser derrubar a requisição inteira, não só o header.
Polling continua sendo caso de rotasIgnoradas, não do agrupamento: a janela reabre a
cada 30s, e um endpoint chamado a cada 5s ainda geraria 120 linhas por hora.
Por que o corpo da requisição não vem ligado
Não é desempenho, é LGPD. Ligado globalmente, o corpo de toda action de todo app passa a ser enviado — e o serviço mascara credencial, não CPF, nome nem dado de saúde. Num grupo farmacêutico isso concentra dado pessoal sensível numa tabela sem endpoint de leitura, sem cifra e sem controle de acesso próprio.
Ligue onde o corpo é o dado da auditoria. Deixe desligado onde ele é só o formulário do usuário.
O que vai parar em mensagemerro
Só quando a chamada falha. E não é a exceção crua: o pacote a reduz ao que serve numa trilha.
System.InvalidOperationException: Pedido 123 sem itens
---> System.ArgumentException: quantidade deve ser maior que zero
at Oriente.Pedidos.Domain.Pedido.Validar(Int32 quantidade)
at Oriente.Pedidos.Application.CriarPedidoHandler.Handle(CriarPedido cmd)
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.Execute()
vira
System.InvalidOperationException: Pedido 123 sem itens | System.ArgumentException: quantidade deve ser maior que zero
A cadeia de causas fica; o stack trace sai. Stack trace responde "onde no código" — pergunta do log da sua aplicação, não da trilha de auditoria — e gasta milhares de caracteres para isso. A trilha responde "o que aconteceu com este usuário nesta rota".
Teto de 2000 caracteres, com ... no corte. E o serviço ainda mascara credencial por cima:
mensagem de exceção carrega valor com a mesma facilidade que corpo de requisição.
Marque o que não pode ser registrado
[HttpPost("login")]
public IActionResult Login([AuditIgnore] LoginRequest requisicao) { ... }
O serviço mascara antes de gravar — em url, requisicao e mensagemerro:
- credencial:
senha,password,token,authorization,bearer,jwt,apiKey,secret,cookie,sessao,pin,cartao,cvv - documento de pessoa:
cpf,cnpj
A máscara olha o nome do campo, não o formato do valor. Casar 11 dígitos pegaria código de
produto e número de nota junto. Por isso /api/clientes/12345678900 — CPF no caminho da rota,
sem nome de campo — não é alcançado: a saída aí é o app não pôr documento em rota.
O que ele não pega: segredo em prosa. "usuário X com senha Y não encontrado" não é
mascarado, porque a máscara casa chave: valor e chave=valor, não texto corrido. Se a sua
exceção monta frase com o valor dentro, o [AuditIgnore] é a defesa, não a redação — mas isso é rede de proteção, não desculpa. O que o [AuditIgnore] cobre nem
sai da sua aplicação.
O que esperar
- Não derruba nem segura o app. O envio sai do caminho da resposta e falha em silêncio, com timeout curto. Auditoria indisponível não vira app indisponível.
- Perde evento se o serviço cair. Não há fila durável nesta versão. Se algum dia não puder perder nenhum registro, o lugar de resolver é aqui, não no app consumidor.
macfica nulo em app web — navegador não entrega MAC. A coluna espera pelos apps que têm essa informação.idusuarioplataformaé o id, não o nome — e quem escolhe é o serviço. O pacote manda as claimsuid,NameIdentifier,sub,Side e-mail, cruas, e nenhuma outra: papel e token nunca saem do app. O serviço fica com a primeira, nessa ordem; sem nenhuma, vale oIdentity.Name, que em vários apps é o nome de exibição. Mudar a ordem é deploy do serviço, não versão nova do pacote.- O IP gravado é o do usuário, mesmo atrás do proxy. O pacote manda o
X-Forwarded-Fore o serviço usa quando a conexão veio da sub-rede do Traefik, configurada no serviço (Acessos__RedesDoProxy). Não precisa configurarForwardedHeadersno app para isso. - Método, status HTTP e trace id vão junto. O trace id é o que liga a linha da trilha ao log do app numa investigação.
- Leitura anônima não entra na trilha (
/health, endpoint público de consulta). GET e HEAD sem usuário nem claim não saem do app: a trilha é de uso de usuário (ADR 0007), e mandar deixava um flood numa rota pública ocupar as vagas de envio dos usuários logados. Anônimo que escreve — redefinir senha, cadastro público — continua, e token de integração semName, que chega com claim, também. Fila cheia vira um aviso por minuto no log, com a contagem.
Ao subir de versão
0.9.0 — o AcessoService precisa estar no ar com a versão de 2026-09-21 antes do primeiro app
com o 0.9.0: o pacote deixa de mandar idUsuarioPlataforma quando há claim, e o serviço antigo
recusa o registro sem ele (400, acesso perdido).
- O agrupamento por tela saiu do pacote (ADR 0008).
JanelaDedupeSegundosePostDeLeituraPorConvencaoficam[Obsolete]e sem efeito: compila, avisa, e pode apagar. - Com claim, o nome de exibição não sai mais do app. O id vem das claims, escolhido no serviço.
- Leitura anônima (GET/HEAD) não sai do app, e a fila cheia avisa uma vez por minuto.
- Das 32 vagas de envio, 8 ficam para escrita e falha: leitura bem-sucedida não as usa.
- O
Mapearpúblico ganhou o parâmetro opcionalpaginadesde o 0.4.0: quem chama direto recompila. Quebra de compatibilidade binária, aceita no 0.x.
No packages depend on Oriente.Acessos.Client.
.NET 8.0
- Audit.NET (>= 32.3.1)
- Audit.WebApi.Core (>= 32.3.1)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
.NET Standard 2.0
- Audit.NET (>= 32.3.1)
- Audit.WebApi.Core (>= 32.3.1)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- System.Security.Cryptography.Xml (>= 9.0.19)
- System.Text.Json (>= 9.0.4)
- System.Text.RegularExpressions (>= 4.3.1)