This content originally appeared on DEV Community and was authored by Carolina Vila-Nova
Publicado originalmente no Medium
Em um cenário de microsserviços e APIs expostas, o gerenciamento seguro e eficiente de tokens de autenticação é crucial para garantir a integridade e o desempenho do sistema.
Recentemente, tive a oportunidade de trabalhar na implementação de uma abordagem que combina cache com refresh automático de tokens, tirando proveito do ecossistema robusto do Go e da alta performance do Redis.
A seguir, detalho essa solução, explorando os desafios iniciais, as estratégias de design e os benefícios alcançados.
Nosso ponto de partida foi a necessidade de autenticação junto a uma API externa para poder gerar — no ambiente desse cliente mas em situações específicas definidas por nossa regra de negócio — mensagens informativas aos usuários.
Uma questão que logo se impôs foi onde armazenar o token obtido, levando em consideração o alto volume de tráfego na nossa API. Armazená-lo diretamente em uma instância do serviço não era uma opção viável, pois colocaria em risco a segurança e a escalabilidade do sistema.
A ideia central foi armazenar o token em um cache distribuído, com um tempo de vida (TTL) equivalente ao tempo de validade do próprio token. Algumas características fizeram com que a escolha do Redis como sistema de cache para esse caso de uso não fosse aleatória, entre elas:
Desempenho: com armazenamento em memória, o Redis oferece tempos de resposta extremamente rápidos, da ordem de milissegundos. Isso é essencial quando se precisa acessar e validar o token de forma rápida em cada requisição. Um cache lento impactaria diretamente a performance da aplicação.
Suporte nativo a TTL: o Redis se encarrega de expirar o token automaticamente após o tempo definido, garantindo que ele seja renovado antes de expirar e prevenindo erros de autenticação. Essa funcionalidade integrada simplifica a lógica de refresh e garante sua confiabilidade.
Atomicidade: as operações acontecem como um todo ou não acontecem, garantindo a consistência dos dados no cache. Isso é importante no contexto do cache de tokens para evitar condições de corrida quando múltiplas instâncias do serviço tentam acessar ou atualizar o token simultaneamente.
Escalabilidade horizontal: o suporte ao clustering permite escalar horizontalmente a capacidade do cache conforme a demanda aumenta sem comprometer a performance e evitando gargalos
A solução
Antes de realizar qualquer requisição à API protegida, a função verifica se existe um token válido no cache.
func GetToken(key string) (string, error) {
cacheToken, err := cache.Get(key)
if err != nil {
return "", err
}
return cacheToken, nil
}
O sistema checa também se esse token ainda é válido, considerando uma margem de segurança (um “buffer”) para evitar requisições com tokens prestes a expirar.
Bibliotecas como a github.com/golang-jwt/jwt/v5 nos permitem decodificar o JWT de forma segura a partir de suas Claims e verificar a data de expiração.
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
func ExpiredToken(tokenString string, buffer time.Duration) (bool, error) {
token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{})
if err != nil {
return true, err // Considera expirado em caso de erro na leitura
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return true, err // Considera expirado se não conseguir extrair claims
}
exp, ok := claims["exp"].(float64)
if !ok {
return true, err // Considera expirado se "exp" não estiver presente
}
expirationTime := time.Unix(int64(exp), 0)
return time.Now().Add(buffer).After(expirationTime), nil
}
//Nota: essa biblioteca oferece mais opções de validação. Nesta implementação,
// usamos ParseUnverified apenas para extrair a data de expiração.
//O token é validado de fato pela API de autenticação
Caso o token não seja encontrado no cache ou tenha expirado (dentro do "buffer" de segurança), o sistema solicita um novo token à API de autenticação e o armazena no cache com o TTL apropriado.
Pode-se ainda usar a biblioteca crypto/rand do Go para gerar um ID único para cada token (um "nonce"), aumentando a segurança e rastreabilidade.
func GetNewToken() (string, error) {
// Lógica para obter novo token da API de autenticação
// ...
return "newToken", nil
}
func StoreTokenCache(key string, token string, ttl time.Duration) error {
err := cache.Set(key, token, ttl)
if err != nil {
return err
}
return nil
}
O TTL do cache garante que o token seja automaticamente atualizado antes de expirar, evitando a necessidade de tentativas repetidas ("retries") em caso de falha na autenticação. A margem de segurança garante que o token seja renovado antes mesmo de expirar, prevenindo erros. Goroutines são utilizadas para realizar o refresh de forma assíncrona, sem bloquear o fluxo principal da aplicação.
func stageRefresh(key string, timeToRefresg time.Duration) {
go func() { // Cria uma goroutine para o refresh
time.Sleep(timeToRefresh) // espera o tempo determinado
newToken, err := GetNewToken()
if err != nil {
fmt.Println("Failed to renew token:", err)
return
}
err = StoreTokenCache(key, newToken, tokenTtl)
if err != nil {
fmt.Println("Failed to store new token in cache:", err)
return
}
fmt.Println("Token refreshed")
}()
}
// Chamada quando o token é gerado/obtido pela primeira vez
stageRefresh(tokenKey, timeToRefresh)
Benefícios da abordagem adotada
Segurança: O token não fica armazenado localmente por tempo indeterminado, reduzindo a janela de oportunidade para ataques.
Escalabilidade: O cache distribuído permite que múltiplas instâncias do serviço compartilhem o mesmo token, eliminando a necessidade de autenticações redundantes.
Resiliência: O refresh automático garante que o token esteja sempre disponível, minimizando interrupções no serviço.
Performance: Evita retries, otimizando o tempo de resposta das requisições.
Em resumo, o uso do cache com refresh automático nos permitiu criar uma solução robusta, segura e escalável para o gerenciamento de tokens, garantindo a disponibilidade e a integridade do serviço.
Fontes
github.com/go-redis/redis/v8: Para interagir com o Redis e armazenar e/ou invalidar os tokens de maneira eficiente
github.com/golang-jwt/jwt/v5: Para manipulação de tokens JWT, incluindo decodificação, validação e, opcionalmente, geração (embora a geração do token seja tipicamente feita pelo servidor de autenticação)
This content originally appeared on DEV Community and was authored by Carolina Vila-Nova
Carolina Vila-Nova | Sciencx (2025-11-12T02:56:34+00:00) Gerenciamento seguro e eficiente de tokens com Go e Redis. Retrieved from https://www.scien.cx/2025/11/12/gerenciamento-seguro-e-eficiente-de-tokens-com-go-e-redis/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.
