As aplicações web modernas exigem sistemas de autenticação robustos para gerenciar o acesso do usuário com segurança. A implementação de JSON Web Tokens (JWT) tornou-se uma abordagem amplamente adotada devido à sua simplicidade e eficácia. Este tutorial demonstra como criar uma API REST usando Node.js e Express, implementando autenticação JWT completa.
O que é JWT?
JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define um formato compacto e autocontido para transmitir informações entre partes como objeto JSON. As informações são verificadas e assinadas digitalmente, tornando-as ideais para autenticação e troca segura de dados.
Um JWT consiste em três partes separadas por pontos: Header.Payload.Signature. O header especifica o algoritmo de assinatura, o payload contém as claims (dados do usuário), e a signature garante a integridade do token.
Pré-requisitos
Antes de começar, certifique-se de ter as seguintes ferramentas instaladas:
- Node.js: Versão 14 ou superior para executar JavaScript no servidor
- MongoDB: Sistema de banco de dados NoSQL para armazenar usuários
- Postman ou Insomnia: Para testar requisições à API
- Editor de código: VS Code ou similar
Criando o Projeto
Crie uma nova pasta para o projeto e navegue até ela:
mkdir jwt-auth-api
cd jwt-auth-apiInicialize um novo projeto Node.js:
npm init -yInstale as dependências necessárias:
npm install express mongoose jsonwebtoken bcryptjs dotenv cors helmet express-rate-limitInstale as dependências de desenvolvimento:
npm install --save-dev nodemonEstrutura do Projeto
Organize o projeto com a seguinte estrutura:
jwt-auth-api/
??? .env
??? app.js
??? models/
? ??? User.js
??? middleware/
? ??? auth.js
??? routes/
? ??? auth.js
??? package.jsonConfiguração de Ambiente
Crie o arquivo .env para variáveis sensíveis:
JWT_SECRET=sua_chave_secreta_muito_forte_aqui
JWT_EXPIRE=7d
MONGO_URI=mongodb://localhost:27017/jwt-auth
PORT=3000Modelo de Usuário
Crie models/User.js com validações e hash de senha:
const mongoose = require(\'mongoose\');
const bcrypt = require(\'bcryptjs\');
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, \'Nome de usuário é obrigatório\'],
unique: true,
trim: true,
minlength: [3, \'Nome deve ter pelo menos 3 caracteres\']
},
email: {
type: String,
required: [true, \'Email é obrigatório\'],
unique: true,
match: [/^\\w+([\\.-]?\\w+)@\\w+([\\.-]?\\w+)(\\.\\w{2,3})+$/, \'Email inválido\']
},
password: {
type: String,
required: [true, \'Senha é obrigatória\'],
minlength: [6, \'Senha deve ter pelo menos 6 caracteres\']
}
}, {
timestamps: true
});
UserSchema.pre(\'save\', async function(next) {
if (!this.isModified(\'password\')) return next();
this.password = await bcrypt.hash(this.password, 12);
next();
});
UserSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
module.exports = mongoose.model(\'User\', UserSchema);Middleware de Autenticação
Crie middleware/auth.js para proteger rotas:
const jwt = require(\'jsonwebtoken\');
const User = require(\'../models/User\');
const auth = async (req, res, next) => {
try {
const token = req.header(\'Authorization\')?.replace(\'Bearer \', \'\');
if (!token) {
return res.status(401).json({ message: \'Token de acesso negado\' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.id).select(\'-password\');
if (!user) {
return res.status(401).json({ message: \'Token inválido\' });
}
req.user = user;
next();
} catch (error) {
res.status(401).json({ message: \'Token inválido\' });
}
};
module.exports = auth;Rotas de Autenticação
Crie routes/auth.js com validações robustas:
const express = require(\'express\');
const jwt = require(\'jsonwebtoken\');
const rateLimit = require(\'express-rate-limit\');
const User = require(\'../models/User\');
const auth = require(\'../middleware/auth\');
const router = express.Router();
// Rate limiting para login
const loginLimiter = rateLimit({
windowMs: 15 60 1000, // 15 minutos
max: 5, // máximo 5 tentativas
message: { message: \'Muitas tentativas de login. Tente novamente em 15 minutos.\' }
});
// Registro de usuário
router.post(\'/register\', async (req, res) => {
try {
const { username, email, password } = req.body;
// Verificar se usuário já existe
const existingUser = await User.findOne({
$or: [{ email }, { username }]
});
if (existingUser) {
return res.status(400).json({
message: \'Usuário ou email já cadastrado\'
});
}
const user = new User({ username, email, password });
await user.save();
const token = jwt.sign(
{ id: user._id },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRE }
);
res.status(201).json({
message: \'Usuário criado com sucesso\',
token,
user: {
id: user._id,
username: user.username,
email: user.email
}
});
} catch (error) {
if (error.name === \'ValidationError\') {
const messages = Object.values(error.errors).map(e => e.message);
return res.status(400).json({ message: messages.join(\', \') });
}
res.status(500).json({ message: \'Erro interno do servidor\' });
}
});
// Login de usuário
router.post(\'/login\', loginLimiter, async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
message: \'Email e senha são obrigatórios\'
});
}
const user = await User.findOne({ email });
if (!user ||
)) {
return res.status(401).json({
message: \'Credenciais inválidas\'
});
}
const token = jwt.sign(
{ id: user._id },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRE }
);
res.json({
message: \'Login realizado com sucesso\',
token,
user: {
id: user._id,
username: user.username,
email: user.email
}
});
} catch (error) {
res.status(500).json({ message: \'Erro interno do servidor\' });
}
});
// Rota protegida - perfil do usuário
router.get(\'/profile\', auth, async (req, res) => {
res.json({
user: {
id: req.user._id,
username: req.user.username,
email: req.user.email,
createdAt: req.user.createdAt
}
});
});
module.exports = router;Aplicação Principal
Configure app.js com segurança e middlewares:
const express = require(\'express\');
const mongoose = require(\'mongoose\');
const cors = require(\'cors\');
const helmet = require(\'helmet\');
require(\'dotenv\').config();
const authRoutes = require(\'./routes/auth\');
const app = express();
// Middlewares de segurança
app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || \'http://localhost:3000\',
credentials: true
}));
app.use(express.json({ limit: \'10mb\' }));
app.use(express.urlencoded({ extended: true }));
// Conexão com MongoDB
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log(\'Conectado ao MongoDB\'))
.catch(err => console.error(\'Erro na conexão:\', err));
// Rotas
app.use(\'/api/auth\', authRoutes);
// Rota de teste
app.get(\'/api/health\', (req, res) => {
res.json({ message: \'API funcionando corretamente\', timestamp: new Date() });
});
// Tratamento de erros
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: \'Algo deu errado!\' });
});
// Rota não encontrada
app.use(\'*\', (req, res) => {
res.status(404).json({ message: \'Rota não encontrada\' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(Servidor executando na porta ${PORT});
});Testando a API
Adicione scripts no package.json:
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "echo \\"Error: no test specified\\" && exit 1"
}
}Execute a aplicação:
npm run devTestando com Postman
Teste os endpoints principais:
- Registro: POST para
/api/auth/registercom JSON contendo username, email e password - Login: POST para
/api/auth/logincom email e password - Perfil: GET para
/api/auth/profilecom token no header Authorization
Melhores Práticas de Segurança
Para produção, implemente as seguintes melhorias:
- HTTPS: Sempre use conexões seguras em produção
- Variáveis de ambiente: Mantenha secrets em variáveis de ambiente seguras
- Rate limiting: Limite tentativas de login e requisições
- Validação de entrada: Use bibliotecas como Joi para validação robusta
- Logs de auditoria: Registre tentativas de login e ações importantes
- Refresh tokens: Implemente sistema de renovação de tokens
Para desenvolvimento web profissional, considere usar serviços de autenticação gerenciados como Auth0 ou AWS Cognito para aplicações críticas que requerem conformidade avançada.
Considerações de Performance
Para otimizar a performance da API:
- Use índices no MongoDB para campos de busca frequente
- Implemente cache Redis para sessões ativas
- Configure connection pooling para o banco de dados
- Use compressão gzip para respostas HTTP
- Monitore métricas de performance com ferramentas como New Relic
A autenticação JWT oferece escalabilidade superior comparada a sessões tradicionais, sendo ideal para arquiteturas de microsserviços e aplicações distribuídas.
Comentários
0Inicie sessão para deixar um comentário
Iniciar sessãoSé el primero en comentar