Introdução à Autenticação
A autenticação é um componente crucial em qualquer aplicação backend, garantindo que apenas usuários autorizados possam acessar determinadas funcionalidades e recursos. Sem uma autenticação adequada, a segurança da aplicação fica comprometida, expondo dados sensíveis e funcionalidades críticas a acessos indevidos.
Neste módulo, vamos entender como implementar a autenticação no nosso sistema de loja simples , utilizando ferramentas modernas como bcrypt para criptografar senhas e JWT (JSON Web Token) para gerenciamento de tokens de autenticação.
Organização Básica do Código para Autenticação
Para manter o código organizado e de fácil manutenção, é importante seguir uma estrutura clara. A seguir, apresentamos uma organização básica das pastas e responsabilidades de cada camada no processo de autenticação:
Estrutura de Pastas Recomendada:
Copy project-root/
│
├── controllers/
│ └── authController.js
├── services/
│ └── authService.js
├── repositories/
│ └── userRepository.js
├── utils/
│ └── hash.js
├── config/
│ └── jwtConfig.js
├── routes/
│ └── authRoutes.js
└── index.js
Descrição das Camadas:
Controller: Define as rotas e lida com as requisições relacionadas à autenticação.
Service: Contém a lógica de negócio para autenticação, como verificação de credenciais.
Repository: Acessa diretamente o banco de dados para buscar ou armazenar informações de usuários.
Utils: Funções utilitárias, como criptografia de senhas.
Config: Configurações gerais, como chaves secretas para tokens.
Passos para Implementar a Autenticação
Vamos seguir um passo a passo para implementar a autenticação no nosso projeto:
1. Preparar as Rotas de Usuário
Primeiro, precisamos configurar os endpoints para cadastro e login dos usuários.
Copy // routes/authRoutes.js
const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');
// Rota para cadastro de usuário
router.post('/cadastro', authController.cadastro);
// Rota para login de usuário
router.post('/login', authController.login);
module.exports = router;
2. Criptografar Senhas com bcrypt
Para garantir a segurança das senhas dos usuários, utilizaremos a biblioteca bcrypt para criptografá-las antes de armazená-las no banco de dados.
Instalação do bcrypt:
Implementação das Funções de Criptografia:
Copy // utils/hash.js
const bcrypt = require('bcrypt');
// Função para gerar hash a partir da senha
exports.generate = (password) => {
const salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt);
};
// Função para comparar senha com hash
exports.compare = (password, hash) => {
return bcrypt.compareSync(password, hash);
};
3. Implementação do Controller de Autenticação
O controller lida com as requisições de cadastro e login, utilizando os serviços apropriados.
Copy // controllers/authController.js
const authService = require('../services/authService');
exports.cadastro = async (req, res) => {
try {
const usuario = await authService.cadastrarUsuario(req.body);
res.status(201).json(usuario);
} catch (error) {
res.status(400).json({ mensagem: error.message });
}
};
exports.login = async (req, res) => {
try {
const token = await authService.loginUsuario(req.body);
res.status(200).json({ token });
} catch (error) {
res.status(400).json({ mensagem: error.message });
}
};
4. Implementação do Service de Autenticação
O service contém a lógica de negócio para cadastro e login dos usuários.
Copy // services/authService.js
const userRepository = require('../repositories/userRepository');
const hash = require('../utils/hash');
const tokenService = require('../services/tokenService');
exports.cadastrarUsuario = async (dados) => {
const { nome, email, telefone, cpf, senha } = dados;
// Verifica se o usuário já existe
const existeUsuario = await userRepository.findByEmail(email);
if (existeUsuario) {
throw new Error('Este email já está cadastrado');
}
// Gera o hash da senha
const senhaHash = hash.generate(senha);
// Cria o novo usuário
const novoUsuario = await userRepository.create({
nome,
email,
telefone,
cpf,
senha: senhaHash,
});
return novoUsuario;
};
exports.loginUsuario = async (dados) => {
const { email, senha } = dados;
// Busca o usuário pelo email
const usuario = await userRepository.findByEmail(email);
if (!usuario) {
throw new Error('Usuário/senha incorretos');
}
// Compara a senha fornecida com o hash armazenado
const senhaValida = hash.compare(senha, usuario.senha);
if (!senhaValida) {
throw new Error('Usuário/senha incorretos');
}
// Gera o token JWT (a ser implementado na página "Tokens e Segurança")
const tokenJWT = tokenService.generateToken({ id: usuario.id, email: usuario.email });
return tokenJWT;
};
5. Implementação do Repository de Usuários
O repository acessa diretamente o banco de dados para buscar ou armazenar informações de usuários.
Copy // repositories/userRepository.js
const prisma = require('../prismaClient');
exports.findByEmail = async (email) => {
return await prisma.usuario.findUnique({
where: { email },
});
};
exports.create = async (dados) => {
return await prisma.usuario.create({
data: dados,
});
};
6. Atualização das Rotas no Servidor
Integrar as rotas de autenticação no servidor principal.
Copy // index.js
require('dotenv').config();
const express = require('express');
const app = express();
const port = 3000;
const produtosRouter = require('./routes/produtos');
const authRoutes = require('./routes/authRoutes');
app.use(express.json());
app.use('/produtos', produtosRouter);
app.use('/auth', authRoutes);
app.listen(port, () => {
console.log(`Servidor rodando em http://localhost:${port}`);
});
Fluxo de Autenticação
Cadastro de Usuário:
O usuário envia seus dados para o endpoint /auth/cadastro
.
A senha é criptografada utilizando bcrypt .
Os dados do usuário são armazenados no banco de dados sem gerar um token.
Login:
O usuário envia suas credenciais para o endpoint /auth/login
.
As credenciais são verificadas e, se válidas, um token JWT é gerado e retornado (a implementação completa do token será detalhada na página "Tokens e Segurança").