Implementação de Autenticação JWT em Aplicações Vue.js

No desenvolvimento de aplicações Vue.js, a integração com um backend para autenticação usando JWT (JSON Web Tokens) é crucial. Vamos explorar como configurar o modelo de dados, endpoints de registro e login, middleware de verificação de token, e o frontend Vue para gerenciar tokens.

Modelo de Dados no MongoDB com Mongoose

O esquema de usuário inclui campos essenciais como nome de usuário, email, senha criptografada, avatar, identidade e timestamps.

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  nomeUsuario: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  senha: {
    type: String,
    required: true
  },
  avatarUrl: {
    type: String,
    default: ''
  },
  tipoIdentidade: {
    type: Number,
    required: true
  },
  criadoEm: {
    type: Date,
    default: Date.now
  },
  atualizadoEm: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('User', userSchema);

Endpoint de Registro com Criptografia de Senha

Para registrar um novo usuário, primeiro verificamos se o email já existe. A senha é criptografada usando uma função de hash com sal antes de salvar.

const crypto = require('crypto');
const UserModel = require('./caminho/para/modelo/user');

function hashSenha(senha) {
  const salSeguro = '!@#$%^&*()';
  const hash = crypto.createHash('sha256');
  hash.update(senha + salSeguro);
  return hash.digest('hex');
}

router.post('/registrar', async (ctx) => {
  const { nomeUsuario, email, senha, avatar, identidade } = ctx.request.body;

  try {
    const usuarioExistente = await UserModel.findOne({ email });
    if (usuarioExistente) {
      ctx.status = 409;
      ctx.body = { status: 'erro', mensagem: 'Email já cadastrado' };
      return;
    }

    const senhaCriptografada = hashSenha(senha);
    const novoUsuario = new UserModel({
      nomeUsuario,
      email,
      senha: senhaCriptografada,
      avatarUrl: avatar,
      tipoIdentidade: identidade
    });

    const usuarioSalvo = await novoUsuario.save();
    ctx.body = { status: 'sucesso', dados: usuarioSalvo };
  } catch (erro) {
    ctx.status = 500;
    ctx.body = { status: 'erro', mensagem: 'Falha no registro' };
  }
});

Endpoint de Login e Geração de Token JWT

No login, verificamos as credenciais e, se válidas, geramos um token JWT com informações do usuário e tempo de expiração.

const jwt = require('jsonwebtoken');
const configSecret = require('./caminho/para/config').jwtSecret;

router.post('/autenticar', async (ctx) => {
  const { email, senha } = ctx.request.body;

  const usuario = await UserModel.findOne({ email });
  if (!usuario) {
    ctx.status = 404;
    ctx.body = { status: 'erro', mensagem: 'Usuário não encontrado' };
    return;
  }

  const senhaFornecida = hashSenha(senha);
  if (usuario.senha !== senhaFornecida) {
    ctx.status = 401;
    ctx.body = { status: 'erro', mensagem: 'Credenciais inválidas' };
    return;
  }

  const payload = {
    id: usuario._id,
    nome: usuario.nomeUsuario,
    email: usuario.email,
    identidade: usuario.tipoIdentidade,
    expiraEm: Date.now() + 3600 * 1000
  };

  const tokenGerado = jwt.sign(payload, configSecret, { expiresIn: '1h' });
  const dadosUsuario = {
    nomeUsuario: usuario.nomeUsuario,
    email: usuario.email,
    avatar: usuario.avatarUrl,
    tipoIdentidade: usuario.tipoIdentidade
  };

  ctx.body = { status: 'sucesso', dados: { ...dadosUsuario, token: `Bearer ${tokenGerado}` } };
});

Middleware de Verificação de Token

Um middleware no backend valida o token JWT em requisições protegidas, verificando sua assinatura e expiração.

const jwt = require('jsonwebtoken');
const UserModel = require('./caminho/para/modelo/user');
const segredoJWT = require('./caminho/para/config').jwtSecret;

async function verificarToken(ctx, next) {
  const rotaAtual = ctx.request.path;

  if (rotaAtual === '/autenticar' || rotaAtual === '/registrar') {
    await next();
    return;
  }

  const cabecalhoAuth = ctx.request.headers.authorization;
  if (!cabecalhoAuth || !cabecalhoAuth.startsWith('Bearer ')) {
    ctx.status = 401;
    ctx.body = { status: 'erro', mensagem: 'Token não fornecido' };
    return;
  }

  const token = cabecalhoAuth.split(' ')[1];
  try {
    const decodificado = jwt.verify(token, segredoJWT);
    const agora = Date.now();

    if (agora > decodificado.expiraEm) {
      ctx.status = 401;
      ctx.body = { status: 'erro', mensagem: 'Token expirado' };
      return;
    }

    const usuarioValido = await UserModel.findById(decodificado.id);
    if (!usuarioValido) {
      ctx.status = 401;
      ctx.body = { status: 'erro', mensagem: 'Usuário não autorizado' };
      return;
    }

    ctx.state.usuario = decodificado;
    await next();
  } catch (erro) {
    ctx.status = 401;
    ctx.body = { status: 'erro', mensagem: 'Token inválido' };
  }
}

module.exports = verificarToken;

Integração no Frontend Vue.js

No Vue.js, após o login, armazenamos o token no localStorage e decodificamos as informações do usuário para uso no estado da aplicação.

import jwtDecode from 'jwt-decode';

methods: {
  async efetuarLogin(email, senha) {
    const resposta = await axios.post('/autenticar', { email, senha });
    if (resposta.data.status === 'sucesso') {
      const token = resposta.data.dados.token;
      localStorage.setItem('jwtToken', token);

      const informacoesUsuario = jwtDecode(token);
      this.$store.commit('definirUsuario', informacoesUsuario);
      this.$router.push('/painel');
    }
  },
  logout() {
    localStorage.removeItem('jwtToken');
    this.$store.commit('limparUsuario');
    this.$router.push('/login');
  }
}

Guia de Rotas no Vue Router

Protegemos rotas no frontend verificando a existência do token no localStorage antes de navegar.

router.beforeEach((para, de, proximo) => {
  const tokenExistente = localStorage.getItem('jwtToken');
  const rotasPublicas = ['/login', '/registrar', '/home'];

  if (rotasPublicas.includes(para.path)) {
    proximo();
  } else if (!tokenExistente) {
    proximo({ path: '/login' });
  } else {
    proximo();
  }
});

Interceptador de Requisições Axios

Configuramos o Axios para incluir automaticamente o token JWT no cabeçalho de autorização em todas as requisições.

axios.interceptors.request.use(
  (configuracao) => {
    const token = localStorage.getItem('jwtToken');
    if (token) {
      configuracao.headers.Authorization = token;
    }
    return configuracao;
  },
  (erro) => {
    return Promise.reject(erro);
  }
);

Tags: vuejs jwt mongodb mongoose koa

Publicado em 6-2 05:46 por Thomas