Capítulo 10: Autenticação Stateless com JWT e Hashing de Senhas
Especialização em Backend com Go & Gin • Gin Framework & Go 1.22+ • GORM, Clean Architecture, Concorrência e Alta Performance
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
B --> C["Controller / Handler"]
C --> D["Service Layer (Regras de Negócio)"]
D --> E["Repository / ORM (Persistência)"]
E --> F["Banco de Dados / Cache"]
subgraph ARQ["Arquitetura do Capítulo"]
G["Conceito: Autenticação Stateless com JWT e Hashing de Senhas"]
H["Segurança, Validação e Resiliência"]
I["Alta Performance e Escalabilidade"]
end
D --> ARQ
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Fundamentos Técnicos de Autenticação Stateless com JWT e Hashing de Senhas
Senhas nunca devem ser armazenadas em texto plano nem com hashes rápidos como MD5/SHA-256 puros — esses algoritmos são desenhados para velocidade, o que os torna vulneráveis a ataques de força bruta em GPU. golang.org/x/crypto/bcrypt resolve isso sendo deliberadamente lento e parametrizável por um fator de custo: bcrypt.GenerateFromPassword([]byte(senha), 12) aplica 2^12 iterações internas do algoritmo Blowfish, e o hash resultante já embute o salt gerado aleatoriamente — não é preciso armazená-lo separadamente. A verificação (bcrypt.CompareHashAndPassword(hash, senhaDigitada)) recalcula o hash com o mesmo salt embutido e compara em tempo constante, evitando timing attacks.
Autenticação stateless com JWT (JSON Web Token) elimina a necessidade de guardar sessão no servidor (nem em memória, nem em Redis) para validar quem é o usuário a cada requisição: o próprio token carrega as informações necessárias, assinadas criptograficamente. Um JWT tem três partes separadas por ponto — header.payload.signature — e com golang-jwt/jwt/v5 a emissão é feita definindo claims customizadas embutidas em jwt.RegisteredClaims (que já traz exp, iat, iss) e assinando com token.SignedString(chaveSecreta) usando HMAC-SHA256. A assinatura garante integridade, não confidencialidade — qualquer um pode decodificar o Base64 do payload e ler seu conteúdo, então dados sensíveis (senha, número de cartão) jamais devem ir dentro do JWT.
type CustomClaims struct {
UserID uint `json:"user_id"`
Email string `json:"email"`
jwt.RegisteredClaims
}
func GerarJWT(userID uint, email string) (string, error) {
claims := CustomClaims{
UserID: userID,
Email: email,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(chaveSecreta)
}
A validação acontece num middleware Gin aplicado às rotas protegidas: extrai o cabeçalho Authorization: Bearer <token>, faz jwt.ParseWithClaims(tokenStr, claims, keyFunc) — que recalcula a assinatura com a chave secreta e verifica se bate com a recebida, além de checar automaticamente o exp — e, se token.Valid for false ou o parsing falhar, responde 401 Unauthorized imediatamente com c.AbortWithStatusJSON, interrompendo a cadeia de middlewares antes de chegar ao handler.
Um padrão de produção fundamental é o par Access Token de curta duração (10-15 minutos) + Refresh Token de vida longa: o access token minimiza a janela de exposição caso seja interceptado, enquanto o refresh token (armazenado com mais cuidado, geralmente httpOnly cookie) permite emitir um novo access token sem forçar novo login. Como JWT é stateless por natureza, revogar um token antes do seu exp natural exige um mecanismo extra — normalmente uma blacklist de JTIs (o jti, identificador único do token) num cache rápido como Redis, consultada no middleware antes de aceitar o token como válido; isso reintroduz um pouco de estado, mas de forma barata e isolada, sem abandonar os ganhos de escalabilidade do modelo stateless.
💻 2. Código de Demonstração Corporativo
package middlewares
func ExigirJWT() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"erro": "token não fornecido"})
return
}
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
claims := &CustomClaims{}
token, err := jwt.ParseWithClaims(tokenStr, claims, func(t *jwt.Token) (interface{}, error) {
return chaveSecreta, nil
})
if err != nil || !token.Valid {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"erro": "token inválido ou expirado"})
return
}
c.Set("user_id", claims.UserID)
c.Next()
}
}
🔗 Recursos Pedagógicos do Capítulo 10
| Recurso Didático | Finalidade | Link de Acesso |
|---|---|---|
| 📊 Slides de Aula | Apresentação visual interativa com Dark Mode e suporte a teclado | Ver Slides |
| 🧠 Quiz Formativo | Teste interativo de fixação com feedback imediato por alternativa | Fazer Quiz |
| 💻 Exemplos de Código | Demonstrações funcionais com código executável | Ver Exemplos |
| 🧩 Exercícios em 4 Níveis | Lista progressiva de fixação com gabarito em bloco colapsável | Resolver Exercícios |
| ⬅️ Capítulo Anterior | 📚 Sumário de Tópicos | Próximo Capítulo ➡️ |