Jump to content

Autenticando uma API em NodeJs com JSON web Tokens


Postagens Recomendadas

  • Administradores

Tópico originalmente criado por: @Jonathan Santos

pic_logo.png.65eb8beee6df841e3c4e567adbcfbc0d.png 

A autenticação é um ponto importante em qualquer aplicação que tenha sua distribuição baseada em usuários. Pensando nisso, trazemos este tutorial de como criar uma autenticação baseada em tokens para autenticar usuários que acessarem sua API. O módulo que vamos utilizar para trabalhar com os tokens é o jsonwebtoken .

O que vamos construir?

Vamos construir uma mini API que terá a autenticação baseada em WebTokens, então teremos um registro de usuários (Utilizando o MongoDB para armazenarmos os usuários), as rotas que serão liberadas apenas se o usuário estiver autenticado, e o middleware que fará a verificação do token para nós.
Para isso vamos precisar de uma maquina com o NPM, NodeJS instalados e também do aplicativo POSTman , com ele vamos conseguir fazer os testes nas rotas de uma forma mais simples.

Para isso vamos precisar da seguinte estrutura de arquivos:

 - app/
---- models/
------------ user.js
- config.js
- package.json
- server.js

Mão na massa!

No seu arquivo ‘package.json’ informe um nome para sua aplicação e deixe o ‘main’ com o nome de seu arquivo server.js

 {
    "name": "api-token",
    "main": "server.js"
}

Instalando os pacotes do npm.
No seu console digite " npm install express body-parser morgan mongoose jsonwebtoken --save "
Dessa forma o npm vai baixar os pacotes e já inserir eles nas dependências do nosso projeto, dentro do package.json

4fab627420c7c18b40427b96cbdcb5c54857091c_2_690x363.png.16125d15ed2ea7352a0b096667e9300d.png

express É o pacote mais simples para criarmos as rotas do nosso app.
body-parser Com ele conseguimos trabalhar com os dados enviados no body das requisições http que nossas rotas estão recebendo
morgan Com ele para cada requisição que nosso app receber, um log é gerado no nosso console. (Usamos para facilitar a visualização do que está sendo feito)
mongoose É uma biblioteca do Node para MongoDB usada para para modelar os dados da sua aplicação utilizando esquemas.

nodemon Pacote usado para subir sua aplicação com a vantagem de que a cada vez que alterar ou criar um arquivo js ele reinicia automaticamente.
jsonwebtoken Este é o pacote responsável pela geração dos tokens de autenticação.

Criando o modelo dos dados do usuário que será salvo no banco.

No arquivo user.js que fica na pasta /app/models/user.js, faça a seguinte configuração:

// Vamos adicionar nosso pacote do mongoose e criar um objeto com schema do nosso usuário que será salvo no banco.
var mongoose = require('mongoose')

var usuarioSchema = new mongoose.Schema({
  name: String,
  password: String
})

// Agora aqui vamos devolver o Schema do usuário para quer acessar este arquivo
module.exports = mongoose.model('Usuario', usuarioSchema)

Criando o nosso banco utilizando o mLab.

  • Acesse o site do mLab  e siga os próximos passos.

  • Clique em Create New ao lado de Create from backup .

6d574463af9e97d09b21a62e91155bd57eacf54b_2_690x310.png.c5470e256b0f33c99a4f1cb750b9cbb8.png

  • Depois selecione a opção Single-node , marque o check de Sandbox , de um nome para seu banco e depois clique em Create new MongoDB deployment .

7f27d6834264c18f890135105d133bb07e0b30ab_2_376x499.png.6cf68648bdf2ec58661fada4adbbf925.png

  • Após este passo, selecione o seu banco e adicione um usuário para ele na aba Users clicando no botão Add database user. Note que marquei em vermelho URL do nosso banco, mongodb://< dbuser >:< dbpassword >@ds163377.mlab.com:63377/token-api onde está < dbuser > e < dbpassword > mude para os dados do usuário que você cadastrou. No meu caso ficou da seguinte forma: mongodb://admin:admin@ds163377.mlab.com:63377/token-api

84e599f42b7d76a930e7cf2733eebd24ae0a5cfb_2_690x351.png.5ac31691516da315f50111636e797990.png

Arquivo config.js

Agora adicionamos nossa variável ‘secret’ que será usada para a geração do token e também adicionamos a url do nosso banco.

module.exports = {
  'secret': 'itsasecret',
  'url': 'mongodb://admin:admin@ds163377.mlab.com:63377/token-api'
  // Caso for utilizar o mongoDB localmente
  // 'url' : 'mongodb://localhost/projeto-api'
}

Arquivo server.js

Nele vamos informar todos os pacotes que vamos utilizar, configurar nossa aplicação com as variáveis importantes, conexão com o banco, criar as rotas básicas como a de criação de um usuário de testes e criar as rotas da API que serão dividas da seguinte forma:

- Rota basica -
POST - /cadastrar
Vamos cadastrar o nome e senha que o usuário nos forneceu.

- API -

POST - /api/authenticate
Vamos verificar o nome  e senha no nosso banco e liberar um Token para o usuário utilizar, caso o nome e senha estejam corretos. Esta rota não vai precisar de um Token, pois é nela que o usuário vai conseguir ele.

GET - /api
Nela vamos mostrar uma mensagem padrão, nessa rota vamos verificar se o usuário requisitou ela usando o Token.

GET - /api/users
Vamos listar todos os usuários cadastrados, porém apenas se quem requisitar estiver utilizando um Token válido. 

Como vai ficar o nosso arquivo server.js

// Vamos referenciar os pacotes que precisamos
var express = require('express')
var app = express()
var bodyParser = require('body-parser')
var morgan = require('morgan')
var mongoose = require('mongoose')

var jwt = require('jsonwebtoken')
var config = require('./config')
var User = require('./app/models/user')

var port = process.env.PORT || 3000
mongoose.Promise = global.Promise
mongoose.connect(config.url) // Conectamos no banco
app.set('superSecret', config.secret) // Variável secret

app.use(bodyParser.urlencoded({
  extended: false
}))
app.use(bodyParser.json())

// Logs das requisições
app.use(morgan('dev'))

// Rotas ===========
// rota basica

app.get('/', (req, res) => {
  res.send('Olá, a API está em http://localhost:' + port + '/api')
})

app.post('/cadastrar', (req, res) => {
  // Criamos um novo usuário utilizando o schema que montamos na pasta models
  var novoUsuario = new User({
    name: req.body.name, // A instrução req.body nos retorna um objeto com os dados que foram enviados através de uma requisição
    password: req.body.password
  })

  novoUsuario.save((err) => {
    if (err) throw err // Se tiver algum erro na hora de salvar, usamos o throw para retornar uma "exception"

    console.log('Usuário cadastrado com sucesso')
    res.json({
      success: true
    }) // Se tudo ocorrer bem, retornamos um json dizendo que deu certo
  })
})

// Rotas da API
// Utiliza uma instancia do Router para as rotas da API
var apiRoutes = express.Router()

// Rota de autenticacao de usuário (POST /api/authenticate)

apiRoutes.post('/authenticate', (req, res) => {
  console.log(req.body)
  User.findOne({
    name: req.body.name
  }, (err, user) => { // O findOne é como um Select, passando um filtro que é o 'name'
    if (err) throw err

    // Verificamos se o usuário existe
    if (!user) {
      res.json({
        success: false,
        message: 'A autenticação falhou, o usuário não foi encontrado :C'
      })
    }
      // Verificamos se a senha é correta
    if (user.password !== req.body.password) {
      res.json({
        success: false,
        message: 'A autenticação falhou, a senha está incorreta :C'
      })
    } else {
      // Se não tiver nenhum erro, então criamos o Token para ele
      var token = jwt.sign(user, app.get('superSecret'), {
        expiresIn: '1440m'
      }) // Aqui dizemos que o Token expira em 1440 minutos (24 hrs)

      // Retornamos um json dizendo que deu certo junto com o seu Token
      res.json({
        success: true,
        message: 'Aproveite seu token!',
        token: token
      })
    }
  })
})

// middleware para validar o Token
apiRoutes.use((req, res, next) => {
  // Aqui vamos verificar o header da requisição, os parametros e o corpo da requisição, procurando o token
  var token = req.body.token || req.query.token || req.headers['x-access-token']

  // Se o token existir
  if (token) {
    // Verificamos se o token está batendo com a nossa Secret
    jwt.verify(token, app.get('superSecret'), (err, decoded) => {
      if (err) {
        return res.json({
          success: false,
          message: 'A autenticação com o token falhou.'
        })
      } else {
        // Se o token estiver válido, então salvamos ele e liberamos o acesso, fazemos o trabalho do porteiro de um prédio aqui.
        req.decoded = decoded
        next()
      }
    })
  } else {
    // Se quem requisitou não informou o token, devolvemos um erro para ele.
    return res.status(403).send({
      success: false,
      message: 'Nenhum token foi informado.'
    })
  }
})

// Rota para nos devolver uma mensagem aleatória (GET /api)
apiRoutes.get('/', (req, res) => {
  res.json({
    message: 'Bem vindo a API mais dahora no mundo!'
  })
})

// Rota que retorna todos os usuários cadastrados no banco (GET /api/users)
apiRoutes.get('/users', (req, res) => {
  User.find({}, (err, users) => { // O que fizemos aqui foi basicamente um Select na "tabela" de usuários
    if (err) { throw err }
    res.json(users)
  })
})

// Aqui dizemos que as rotas terão o prefixo /api
app.use('/api', apiRoutes)

// Inicia o servidor
app.listen(port)
console.log('Servidor iniciado em http://localhost:' + port)

 

Para executar utilize no console ‘nodemon server.js’

Vamos por partes! ( Rotas da API)

  • Cadastro de usuário
app.post('/cadastrar', (req, res) => {
  // Criamos um novo usuário utilizando o schema que montamos na pasta models
  var novoUsuario = new User({
    name: req.body.name, // A instrução req.body nos retorna um objeto com os dados que foram enviados através de uma requisição
    password: req.body.password
  })

  novoUsuario.save((err) => {
    if (err) throw err // Se tiver algum erro na hora de salvar, usamos o throw para retornar uma "exception"

    console.log('Usuário cadastrado com sucesso')
    res.json({
      success: true
    }) // Se tudo ocorrer bem, retornamos um json dizendo que deu certo
  })
})

Teste no POSTman

c090d1baeb2bd13615d7840b7d9d749556cbf3ab.png.1be372b48ca8a89490c1f798a7c7ed23.png

  • Listagem de usuários
// Rota que retorna todos os usuários cadastrados no banco (GET /api/users)
apiRoutes.get('/users', (req, res) => {
  User.find({}, (err, users) => { // O que fizemos aqui foi basicamente um Select na "tabela" de usuários
    if (err) { throw err }
    res.json(users)
  })
})

Teste no POSTman

e93e86ef4f904e5baaccf7e63bb3c77b9246c0bf_2_690x466.png.6a8ff124316b18806c1caeed90fb1b92.png

  • Autenticação do token.
// Rota de autenticacao de usuário (POST /api/authenticate)
apiRoutes.post('/authenticate', (req, res) => {
  console.log(req.body)
  User.findOne({
    name: req.body.name
  }, (err, user) => { // O findOne é como um Select, passando um filtro que é o 'name'
    if (err) throw err

    // Verificamos se o usuário existe
    if (!user) {
      res.json({
        success: false,
        message: 'A autenticação falhou, o usuário não foi encontrado :C'
      })
    }
      // Verificamos se a senha é correta
    if (user.password !== req.body.password) {
      res.json({
        success: false,
        message: 'A autenticação falhou, a senha está incorreta :C'
      })
    } else {
      // Se não tiver nenhum erro, então criamos o Token para ele
      var token = jwt.sign(user, app.get('superSecret'), {
        expiresIn: '1440m'
      }) // Aqui dizemos que o Token expira em 1440 minutos (24 hrs)

      // Retornamos um json dizendo que deu certo junto com o seu Token
      res.json({
        success: true,
        message: 'Aproveite seu token!',
        token: token
      })
    }
  })
})

Testes no POSTman

  • Sucesso

6924181b5cc80beaa774d7fc85f8d11c465b752d_2_559x500.png.8454d76946138a09c47fc0570cab85be.png

 

  • Senha inválida

6b23920028ac32090f85e19165d2d586f95ddaec_2_690x458.png.9fcdb97c3c60c6b5237f7265d983dedf.png

  • Usuário inválido

a2a2affc9f6f341cfffe8a4571e283b8b978a79c.png.770a317be97ddd88c12b324f30b10b67.png

 

Middleware

Neste passo vamos montar a proteção das nossas rotas.

// middleware para validar o Token
apiRoutes.use((req, res, next) => {
  // Aqui vamos verificar o header da requisição, os parametros e o corpo da requisição, procurando o token
  var token = req.body.token || req.query.token || req.headers['x-access-token']

  // Se o token existir
  if (token) {
    // Verificamos se o token está batendo com a nossa Secret
    jwt.verify(token, app.get('superSecret'), (err, decoded) => {
      if (err) {
        return res.json({
          success: false,
          message: 'A autenticação com o token falhou.'
        })
      } else {
        // Se o token estiver válido, então salvamos ele e liberamos o acesso, fazemos o trabalho do porteiro de um prédio aqui.
        req.decoded = decoded
        next()
      }
    })
  } else {
    // Se quem requisitou não informou o token, devolvemos um erro para ele.
    return res.status(403).send({
      success: false,
      message: 'Nenhum token foi informado.'
    })
  }
})
 

Testes no POSTman

  • Sucesso

1371635163_e93e86ef4f904e5baaccf7e63bb3c77b9246c0bf_2_690x466(1).png.dd92f436734d1cd87a3e07ef0a870203.png

  • Erro (Token inválido)

76ab6382e32c427264996e8b4960e1dc223f4bdf.png.518218c81be5004e19e48f31b5105350.png

  • Erro (Token vazio)

a70374692a82cbe30bd02bc9672260c45fdb4ea6.png.571ec55295ee7662bfeae451393bd545.png

Conclusão

Recentemente encontrei este tutorial em um site e resolvi traduzir ele e deixar de uma forma mais simples, ele nos dá uma boa visão de como proteger nossas rotas utilizando o JSON Web Token, caso queira algum tutorial específico nos deixe um comentário, valeu pessoal!

Já ia me esquecendo, caso queira verificar o projeto completo baixe o anexo do post, você também pode clonar ele para sua maquina pelo git clone https://github.com/nulldreams/autenticacao-api-jwt.git

 

  • Curtir 1
Link to comment
Compartilhe em outros sites

Crie uma conta ou entre para comentar 😀

Você precisa ser um membro para deixar um comentário.

Crie a sua conta

Participe da nossa comunidade, crie sua conta.
É bem rápido!

Criar minha conta agora

Entrar

Você já tem uma conta?
Faça o login agora.

Entrar agora
×
×
  • Create New...