Douglas Legramante

Douglas Legramante

Informática

JWT + MySQL


Sumário

  1. O que muda (e o que não muda)
  2. A tabela usuarios
  3. Novos pacotes: bcrypt e jsonwebtoken
  4. Registro
  5. Login
  6. 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
);
  • UNIQUE em email garante que dois usuários não cadastrem o mesmo e-mail.
  • senha_hash guarda o resultado do bcrypt — nunca a senha em texto puro.

3. Novos pacotes: bcrypt e jsonwebtoken

npm install bcrypt jsonwebtoken
PacotePapel
bcryptTransforma a senha em um hash — um texto embaralhado que não pode ser revertido de volta à senha original
jsonwebtokenGera 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) — o 10 é 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.compare confere 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.