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 ➡️