Tokens JWT Explicados: Autenticação para Aplicações Web Modernas
· 12 min de leitura
Índice
- O Que É JWT?
- Estrutura do JWT Detalhada
- Como Funciona a Autenticação JWT
- JWT vs Autenticação Baseada em Sessão
- Implementando JWT na Sua Aplicação
- Melhores Práticas de Segurança
- Vulnerabilidades Comuns e Como Preveni-las
- Estratégias de Renovação de Token
- Onde Armazenar JWTs
- Considerações de Performance
- Perguntas Frequentes
- Artigos Relacionados
JSON Web Tokens (JWT) tornaram-se o padrão de facto para autenticação em aplicações web modernas. Eles fornecem uma maneira compacta e segura para URL de representar declarações entre duas partes, permitindo autenticação sem estado que escala horizontalmente sem armazenamento de sessão compartilhado.
Seja você construindo uma API REST, uma arquitetura de microsserviços ou uma aplicação de página única, entender JWTs é essencial para implementar autenticação segura e escalável. Este guia abrangente explica como JWTs funcionam, sua estrutura, considerações de segurança e melhores práticas prontas para produção.
O Que É JWT?
JWT (pronunciado "jot") é um padrão aberto (RFC 7519) que define um formato compacto e autocontido para transmitir informações com segurança entre partes como um objeto JSON. A informação é assinada digitalmente usando uma chave secreta (HMAC) ou um par de chaves pública/privada (RSA ou ECDSA), garantindo a integridade e autenticidade do token.
Diferentemente da autenticação tradicional baseada em sessão onde o servidor mantém o estado da sessão em memória ou banco de dados, JWTs são sem estado. Todas as informações necessárias para verificar a identidade do usuário estão contidas no próprio token.
Casos de Uso Principais para JWT
- Autenticação: Após um usuário fazer login, o servidor emite um JWT que o cliente inclui em requisições subsequentes para provar sua identidade
- Autorização: JWTs podem carregar funções de usuário, permissões e escopos, permitindo que servidores tomem decisões granulares de controle de acesso sem consultas ao banco de dados
- Troca de informações: A natureza assinada dos JWTs garante que o remetente é quem afirma ser e que os dados não foram adulterados durante a transmissão
- Single Sign-On (SSO): JWTs permitem autenticação perfeita entre múltiplos domínios e serviços
- Autenticação de API: Aplicativos móveis e integrações de terceiros podem autenticar requisições de API sem manter sessões no servidor
Dica profissional: Use nosso Decodificador JWT para inspecionar e validar tokens durante o desenvolvimento. Ele ajuda você a entender a estrutura e detectar problemas comuns antes de chegarem à produção.
Estrutura do JWT Detalhada
Um JWT consiste em três partes distintas separadas por pontos (.): cabeçalho.payload.assinatura
Veja como um JWT completo se parece:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRGV2ZWxvcGVyIiwiZW1haWwiOiJqYW5lQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEwNTkwNDAwLCJleHAiOjE3MTA2NzY4MDB9.4Adcj0mYZ8s5vxjKvV8pF7jKX9s8vZ5xJ3kL9mN2pQ4
Parte 1: Cabeçalho
O cabeçalho normalmente contém dois campos que descrevem as operações criptográficas do token:
{
"alg": "HS256",
"typ": "JWT"
}
algespecifica o algoritmo de assinatura (HS256, RS256, ES256, etc.)typdeclara o tipo de token, que é sempre "JWT"
Este objeto JSON é codificado em Base64Url para formar a primeira parte do JWT. A codificação o torna seguro para URL e compacto para transmissão em cabeçalhos HTTP.
Parte 2: Payload
O payload contém declarações—afirmações sobre o usuário e metadados adicionais. As declarações são categorizadas em três tipos:
{
"sub": "1234567890",
"name": "Jane Developer",
"email": "[email protected]",
"role": "admin",
"iat": 1710590400,
"exp": 1710676800
}
Declarações registradas são predefinidas pela especificação JWT e fornecem informações padrão:
iss(issuer): Identifica quem emitiu o tokensub(subject): Identifica o assunto do token (geralmente o ID do usuário)aud(audience): Identifica os destinatários pretendidosexp(expiration time): Timestamp Unix quando o token expiranbf(not before): Token não é válido antes deste horárioiat(issued at): Quando o token foi criadojti(JWT ID): Identificador único para o token
Declarações públicas são declarações personalizadas que devem ser definidas no Registro IANA de JSON Web Token ou usar nomes resistentes a colisão (como URLs).
Declarações privadas são declarações personalizadas acordadas entre as partes, como role, permissions ou organizationId.
Importante: O payload é apenas codificado em Base64Url, não criptografado. Nunca armazene informações sensíveis como senhas, números de cartão de crédito ou números de segurança social em um payload JWT.
Parte 3: Assinatura
A assinatura garante que o token não foi alterado. Ela é criada pegando o cabeçalho codificado, payload codificado, uma chave secreta e aplicando o algoritmo especificado no cabeçalho:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
A assinatura permite que o destinatário verifique que o remetente é quem afirma ser e que a mensagem não foi alterada no caminho.
Comparação de Algoritmos de Assinatura
| Algoritmo | Tipo | Nível de Segurança | Caso de Uso | Gerenciamento de Chaves |
|---|---|---|---|---|
| HS256 | Simétrico (HMAC) | Bom | Serviço único, APIs internas | Segredo compartilhado |
| RS256 | Assimétrico (RSA) | Muito Bom | Múltiplos serviços, APIs públicas | Par de chaves pública/privada |
| ES256 | Assimétrico (ECDSA) | Excelente | Requisitos de alta segurança | Par de chaves pública/privada |
| PS256 | Assimétrico (RSA-PSS) | Excelente | Aplicações modernas | Par de chaves pública/privada |
Como Funciona a Autenticação JWT
Entender o fluxo completo de autenticação ajuda você a implementar JWTs corretamente e solucionar problemas de forma eficaz.
O Fluxo Completo de Autenticação
- Login do Usuário: O cliente envia credenciais (nome de usuário/senha) para o endpoint de autenticação
- Verificação de Credenciais: O servidor valida as credenciais contra o banco de dados
- Geração de Token: Após autenticação bem-sucedida, o servidor cria um JWT contendo informações do usuário e declarações
- Entrega do Token: O servidor envia o JWT de volta ao cliente (normalmente no corpo da resposta)
- Armazenamento do Token: O cliente armazena o JWT (em memória, localStorage ou cookies httpOnly)
- Requisições Autenticadas: Para requisições subsequentes, o cliente inclui o JWT no cabeçalho Authorization
- Verificação do Token: O servidor valida a assinatura do JWT e verifica a expiração
- Acesso Concedido: Se válido, o servidor processa a requisição usando as declarações no token
Exemplo de Requisição de Autenticação
POST /api/auth/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "[email protected]",
"password": "securePassword123"
}
Exemplo de Resposta de Autenticação
HTTP/1.1 200 OK
Content-Type: application/json
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
Exemplo de Requisição de API Autenticada
GET /api/users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT vs Autenticação Baseada em Sessão
Escolher entre JWT e autenticação baseada em sessão depende da arquitetura, escala e requisitos da sua aplicação.
| Aspecto | JWT | Baseada em Sessão |
|---|---|---|
| Estado | Sem estado (servidor não armazena tokens) | Com estado (servidor armazena dados de sessão) |
| Escalabilidade | Excelente (não precisa de armazenamento compartilhado) | Requer armazenamento de sessão (Redis, banco de dados) |
| Revogação | Difícil (requer lista negra ou expiração curta) | Fácil (deletar sessão do armazenamento) |
| Tamanho | Maior (enviado com cada requisição) | Menor (apenas ID de sessão) |
| Cross-domain | Fácil (compatível com CORS) | Complexo (restrições de domínio de cookie) |
| Apps móveis | Ideal (não precisa de suporte a cookies) | Desafiador (manipulação de cookies) |
| Segurança | Requer implementação cuidadosa | Padrões bem estabelecidos |
Quando Usar JWT
- Construindo microsserviços que precisam compartilhar autenticação
- Desenvolvendo aplicações móveis ou SPAs
- Criando APIs públicas para integrações de terceiros
- Implementando Single Sign-On (SSO) entre múltiplos domínios
- Escalando horizontalmente sem armazenamento de sessão compartilhado
Quando Usar Sessões
- Construindo aplicações web tradicionais renderizadas no servidor
- Requerendo capacidades de revogação imediata de token
- Trabalhando com dados sensíveis que precisam de controle no servidor
- Minimizando largura de banda (sessões usam cookies menores)
- Implementando recursos complexos de gerenciamento de sessão
Implementando JWT na Sua Aplicação
Vamos percorrer exemplos práticos de implementação em linguagens de programação e frameworks populares.
Node.js com Express
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
const SECRET_KEY = process.env.JWT_SECRET;
// Endpoint de login
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
// Verificar credenciais (pseudo-código)
const user = await verifyCredentials(email, password);
if (!user) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
// Gerar JWT
const token = jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role
},
SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ accessToken: token });
});
// Middleware de autenticação
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Nenhum token fornecido' });
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token inválido' });
}
req.user = user;
next();
});
};
// Rota protegida
app.get('/api/users/profile', authenticateToken, (req, res) => {
res.json({ user: req.user });
});
Python com Flask
from flask import Flask, request, jsonify
import jwt
import datetime
import os
app = Flask(__name__)
SECRET_KEY = os.getenv('JWT_SECRET')
@app.route('/api/auth/login', methods=['POST'])
def login():
data = request.get_json()
email = data.get('email')
password = data.get('password')
# Verificar credenciais
user = verify_credentials(email, password)
if not user:
return jsonify({'error': 'Credenciais inválidas'}), 401
# Gerar JWT
token = jwt.encode({
'sub': user['id'],
'email': user['email'],
'role': user['role'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, SECRET_KEY, algorithm='HS256')
return jsonify({'accessToken': token})
def token_required(f):
def decorator(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Nenhum token fornecido'}), 401
try:
token = token.split(' ')[1]
data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
request.user = data
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token expirado'}), 403
except jwt.InvalidTokenError:
return jsonify({'error': 'Token inválido'}), 403
return f(*args, **kwargs)
decorator.__name__ = f.__name__
return decorator
@app.route('/api/users/profile')
@token_required
def profile():
return jsonify({'user': request.user})
Dica rápida: Sempre use variáveis de ambiente para chaves secretas. Nunca as codifique diretamente no seu código-fonte ou as envie para controle de versão. Use ferramentas como Gerador de Senhas para criar segredos fortes.