Técnico em Informática · Back-End II
08-JWT + MySQL
Materiais desta aula
JWT + MySQL
Sumário
- O que muda (e o que não muda)
- A tabela usuarios
- Novos pacotes: bcrypt e jsonwebtoken
- Registro
- Login
- Protegendo rotas
1. O que muda (e o que não muda)
O fluxo de autenticação continua exatamente o mesmo de Back-end I:
1. Registro → usuário cria conta, senha é criptografada
2. Login → servidor confere a senha e gera um token JWT
3. Requisição → cliente envia o token no header Authorization
4. Middleware → rota protegida só responde se o token for válido
A única diferença: os passos 1 e 2 agora conversam com o MySQL, através da tabela usuarios, em vez de um arquivo ou array em memória.
2. A tabela usuarios
CREATE TABLE usuarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(120) UNIQUE NOT NULL,
senha_hash VARCHAR(255) NOT NULL
);
UNIQUEememailgarante que dois usuários não cadastrem o mesmo e-mail.senha_hashguarda o resultado do bcrypt — nunca a senha em texto puro.
3. Novos pacotes: bcrypt e jsonwebtoken
npm install bcrypt jsonwebtoken
| Pacote | Papel |
|---|---|
bcrypt | Transforma a senha em um hash — um texto embaralhado que não pode ser revertido de volta à senha original |
jsonwebtoken | Gera e confere o token JWT — o "crachá" que o cliente apresenta em cada requisição autenticada |
4. Registro
services/usuariosService.js
export async function criar(usuario) {
const { nome, email, senha } = usuario;
const senha_hash = await bcrypt.hash(senha, 10);
const [r] = await pool.query(
'INSERT INTO usuarios (nome, email, senha_hash)' +
' VALUES (?, ?, ?)',
[nome, email, senha_hash]
);
return r.insertId;
}
bcrypt.hash(senha, 10)— o10é o "custo" do hash: quanto maior, mais lento (e mais seguro) o processo.
export async function buscarPorEmail(email) {
const [rows] = await pool.query(
'SELECT * FROM usuarios WHERE email = ?', [email]
);
return rows[0];
}
controllers/usuariosController.js — registrar
export async function registrar(req, res, next) {
try {
const { email } = req.body;
const existe = await service.buscarPorEmail(email);
if (existe) {
return res.status(400).json({ erro: 'email em uso' });
}
const id = await service.criar(req.body);
res.status(201).json({ id, email });
} catch (erro) { next(erro); }
}
5. Login
export async function login(req, res, next) {
try {
const { email, senha } = req.body;
const usuario = await service.buscarPorEmail(email);
if (!usuario) {
return res.status(401).json({ erro: 'invalido' });
}
const ok = await bcrypt.compare(senha, usuario.senha_hash);
if (!ok) {
return res.status(401).json({ erro: 'invalido' });
}
const token = jwt.sign(
{ id: usuario.id, email: usuario.email },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
} catch (erro) { next(erro); }
}
Dois pontos importantes:
bcrypt.compareconfere a senha digitada contra o hash salvo, sem nunca "descriptografar" nada — não é possível descriptografar um hash, só comparar.- 401, não 404 — se o e-mail não existe ou a senha está errada, a resposta é a mesma ("credenciais inválidas"). Isso evita que alguém descubra, por tentativa e erro, quais e-mails estão cadastrados no sistema.
6. Protegendo rotas
middlewares/autenticar.js
import jwt from 'jsonwebtoken';
export function autenticar(req, res, next) {
const header = req.headers.authorization;
if (!header) {
return res.status(401).json({ erro: 'sem token' });
}
const token = header.split(' ')[1];
try {
req.usuario = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (erro) {
res.status(401).json({ erro: 'token invalido' });
}
}
Aplicando em uma rota
import { autenticar } from '../middlewares/autenticar.js';
router.delete(
'/produtos/:id', autenticar, controller.deletar
);
Só quem manda um token válido no header Authorization consegue deletar um produto. As outras rotas continuam abertas — a proteção é seletiva, rota por rota.
Para a próxima aula
Postman Avançado. Collections, variáveis de ambiente, e organizando os testes da StockAPI de forma profissional.