Capítulo 11: Autorização Baseada em Papéis (RBAC) e Claims Customizadas
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["Requisição Autenticada (c.Set('userRole', 'EDITOR'))"] --> B["Gin Router"]
B --> C["Middleware RBAC: ExigirPapeis('ADMIN', 'FINANCEIRO')"]
subgraph AVALIACAO["Avaliação de Políticas de Acesso"]
C --> D["role, existe := c.Get('userRole')"]
D --> E{"Papel do Usuário está na Lista Autorizada?"}
E -->|"Não ('EDITOR' não autorizado)"| F["c.AbortWithStatusJSON(403 Forbidden)"]
E -->|"Sim (Privilégio Concedido)"| G["c.Next()"]
end
F --> H["Log de Auditoria de Segurança (SIEM / Alerta)"]
G --> I["Handler da Rota Crítica (ex: Deletar Fatura)"]
I --> J["HTTP 200 OK / 204 No Content"]
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:#ede7f6,stroke:#7e57c2,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#ffebee,stroke:#e53935,stroke-width:2px
style G fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style H fill:#b71c1c,stroke:#b71c1c,stroke-width:2px,color:#fff
style I fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style J fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
🏛️ 1. Autenticação vs Autorização e o Modelo RBAC
No design de segurança de APIs REST, é indispensável manter uma distinção clara entre duas responsabilidades que frequentemente são confundidas:
- Autenticação (AuthN): Responde à pergunta “Quem é você?”. Verifica a identidade do usuário através de credenciais (senhas, certificados, tokens JWT). Se falhar, o código de status HTTP correto é
401 Unauthorized. - Autorização (AuthZ): Responde à pergunta “O que você pode fazer?”. Avalia se o usuário já autenticado possui as permissões necessárias para acessar o recurso específico ou executar uma mutação (como excluir um registro). Se falhar, o código de status HTTP correto é obrigatoriamente
403 Forbidden.
Modelos de Controle de Acesso
| Modelo | Nome Completo | Mecânica de Funcionamento | Quando Adotar |
|---|---|---|---|
| RBAC | Role-Based Access Control | Usuários recebem Papéis estáticos (Admin, Editor, Viewer), e as rotas exigem papéis específicos. |
Padrão corporativo consolidado para a maioria das aplicações SaaS e ERPs. |
| ABAC | Attribute-Based Access Control | Avalia atributos dinâmicos do usuário, do recurso e do ambiente (ex: departamento, horário, IP de origem). | Sistemas de alta segurança governamental ou bancária com políticas complexas. |
| PBAC | Policy-Based Access Control | Políticas declaradas em linguagens externas (ex: Open Policy Agent / Rego). | Arquiteturas de microsserviços em grande escala com governança centralizada. |
⚙️ 2. Middleware RBAC Parametrizado no Gin
Em Go, criamos middlewares configuráveis através de funções que recebem os parâmetros de autorização e retornam um gin.HandlerFunc (padrão closure):
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// ExigirPapeis valida se o usuário autenticado possui ao menos um dos papéis informados
func ExigirPapeis(papeisPermitidos ...string) gin.HandlerFunc {
// Converte os papéis permitidos em um mapa hash O(1) para busca em tempo constante
mapaPapeis := make(map[string]struct{}, len(papeisPermitidos))
for _, papel := range papeisPermitidos {
mapaPapeis[papel] = struct{}{}
}
return func(c *gin.Context) {
valorRole, existe := c.Get("userRole")
if !existe {
// Significa que o middleware de Autenticação não rodou antes deste
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"erro": "requisição não autenticada no contexto",
})
return
}
roleUsuario, ok := valorRole.(string)
if !ok {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"erro": "falha ao processar papel do usuário",
})
return
}
// Valida se o papel do usuário está no conjunto permitido
if _, permitido := mapaPapeis[roleUsuario]; !permitido {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"erro": "acesso negado para o perfil atual",
"necessario": papeisPermitidos,
"seu_papel": roleUsuario,
})
return
}
c.Next()
}
}
🚀 3. Aplicação em Grupos de Rotas no Gin
A organização idiomática no Gin utiliza grupos de rotas com encadeamento de middlewares de segurança:
package routes
import (
"github.com/gin-gonic/gin"
"meu-modulo/handlers"
"meu-modulo/middleware"
)
func RegistrarRotasSeguras(r *gin.Engine, relatorioHandler *handlers.RelatorioHandler) {
// Grupo base que exige token JWT válido para todas as rotas
apiAutenticada := r.Group("/api/v1")
apiAutenticada.Use(middleware.AutenticacaoJWT())
{
// Rotas de leitura permitidas para qualquer usuário logado
apiAutenticada.GET("/perfil", handlers.ObterPerfilProprio)
// Subgrupo restrito: apenas Gestores e Administradores
gestao := apiAutenticada.Group("/relatorios")
gestao.Use(middleware.ExigirPapeis("GESTOR", "ADMIN"))
{
gestao.GET("/financeiro", relatorioHandler.ExportarFinanceiro)
}
// Subgrupo ultra-crítico: estritamente Administrador Root
admin := apiAutenticada.Group("/admin")
admin.Use(middleware.ExigirPapeis("ADMIN"))
{
admin.DELETE("/usuarios/:id", handlers.ExpurgarUsuario)
admin.POST("/sistema/reiniciar", handlers.ReiniciarSistema)
}
}
}
🛡️ 4. Permissões Granulares baseadas em Claims e Auditoria
Em plataformas SaaS com permissões finas (onde um usuário pode ter a permissão faturamento:emitir mas não faturamento:cancelar), as claims do JWT podem carregar um slice de strings com os códigos das permissões (permissions: ["faturamento:emitir", "clientes:ler"]).
Registro de Tentativas Não Autorizadas (Security Audit)
Toda resposta 403 Forbidden deve ser tratada como um evento de segurança corporativa:
- Deve ser emitida uma linha de log estruturado contendo:
Timestamp,IP de Origem,UserID,Tentativa de Rota,Role RequeridaeUser-Agent. - Se o mesmo IP disparar múltiplos 403 em uma janela de 60 segundos, o sistema de Rate Limiting e WAF deve temporariamente bloquear aquele tráfego para conter potenciais varreduras maliciosas.