Oi, pessoal. Conheça Raphael Girão, instrutor da Alura.
Raphael se identifica como uma pessoa morena. Tem olhos castanhos-claros e cabelo preto curto. Está com uma blusa preta e sentado em uma cadeira gamer também preta. Ao fundo, uma estante com livros, quadro e outras decorações.
Se você quer aprender mais sobre segurança com Node.js, está no lugar certo!
Você vai aprender a:
Conheceremos todos esses assuntos na nossa API de estoque de supermercado, onde você vai poder cadastrar, listar, editar ou deletar produtos.
Além disso, vamos entender como cadastrar pessoas usuárias e também como cadastrar perfis para cada uma dessas pessoas - como gerência, vendedor(a) e estoquista. Assim, cada pessoa vai ter diferentes permissões.
Se você não acompanha a formação de Autenticação, testes e segurança em Node.js, você vai precisar de alguns pré-requisitos:
Na plataforma, temos várias atividades, fórum e Discord da Alura, onde você pode tirar dúvidas e acessar conteúdos exclusivos. Vamos codar?
Vamos dar início ao nosso curso de segurança com Node.js.
No VSCode, temos nossa API de produtos de supermercado que atualmente já cadastra, lista, edita e deleta produtos. Porém, todas essas ações são realizadas por pessoas usuárias, o que ainda não temos na nossa aplicação.
Diante disso, vamos dar início ao fazer cadastro de pessoas usuárias. Para isso, vamos abrir o terminal com "Ctrl + `" e fazer a criação da tabela no banco de dados.
No terminal, vamos utilizar um CLI do Squelize para fazer a criação da nossa tabela. Digitamos o comando sequelize model:create
e usamos o alias --name
para definir o nome da nossa tabela, que será usuarios
.
Também vamos utilizar um alias chamado --attributes
que define os parâmetros e informações que a pessoa usuária vai ter. Vamos definir nome
, email
e senha
, todos acompanhados de dois pontos e o tipo string
. Lembre-se de separá-los por vírgula.
sequelize model:create --name usuarios --attributes nome:string,email:string,senha:string
New model was created at C:\alura\seguranca-nodejs\api\models\usuarios.js
New migration was created at C:\alura\seguranca-nodejs\api\migrations\20230216210729-create-usuarios.js
Agora que temos a nossa tabela criada no banco de dados. Podemos acessar o diretório dos nossos arquivos para verificar alguns novos arquivos.
Em "api > migrations", temos um novo arquivo chamado 20230216210729-create-usuarios.js
criado para a nossa migration de produtos.
Dentro dessa migration, temos todos os campos criados: id
, nome
, email
, senha
, createAt
, updatedAt
. Eles foram gerados automaticamente a partir do nosso comando com essas informações pelo terminal.
20230216210729-create-usuarios.js
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('usuarios', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
nome: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
senha: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('usuarios');
}
};
Vamos fazer um pequeno ajuste no id
da nossa tabela. Atualmente, o tipo está como inteiro. Porém, isso cria uma fragilidade no nosso sistema, porque qualquer pessoa que tentar acessar a requisição do sistema pode verificar e saber quantos registros temos na nossa base de dados.
Portanto, vamos fazer uma alteração em type
do campo id
. Ao invés de utilizar um id
do tipo INTEGER
, vamos usar um id
do tipo UUID
que é um hash. Assim, criamos uma segurança e evitamos possíveis ataques.
Ainda em id
, vamos remover o autoIncrement
, pois não vamos utilizá-lo. Vamos definir um defaultValue
para a coluna id
do tipo Sequelize.UUID
- assim vai ser do mesmo tipo definido para a coluna.
// código omitido…
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUID
},
// código omitido…
Agora que já fizemos todos os ajustes na nossa migration, garantimos uma segurança maior na nossa tabela.
Vamos acessar o outro arquivo que foi criado. Acessamos "api > models > usuarios.js
".
usuarios.js
:
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class usuarios extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
usuarios.init({
nome: DataTypes.STRING,
email: DataTypes.STRING,
senha: DataTypes.STRING
}, {
sequelize,
modelName: 'usuarios',
});
return usuarios;
};
Conseguimos verificar que a model de usuarios
foi criada com nome
, email
e senha
. Os três do tipo STRING
, como tínhamos definido anteriormente.
Logo abaixo, temos algumas informações como o nome da pessoa tabela em modelName
, onde também vamos fazer um pequeno ajuste.
As pessoas usuárias têm uma senha, mas não devemos retornar essas senhas para qualquer pessoa que fizer um select
na nossa base para retornar todas as informações. Afinal, a senha é um dado sensível e não é bom ter um acesso - a menos que seja controlado.
Abaixo de modelName
, vamos colocar um deafultScope
para adicionar limitações na nossa model, aceitando um objeto.
Nesse objeto, vamos digitar attributes
para definir em quais atributos queremos adicionar alguma ação. Dentro dele, vamos adicionar chaves e uma função chamada exclude
para excluir a coluna, recebendo um array onde definimos a coluna a ser excluída quando fizer uma interação. Nesse caso, a nossa senha
entre aspas simples.
usuarios.init({
nome: DataTypes.STRING,
email: DataTypes.STRING,
senha: DataTypes.STRING
}, {
sequelize,
modelName: 'usuarios',
defaultScope: {
attributes: {
exclude: ['senha']
}
}
});
Agora que fizemos ajustes tanto na migration como na model, garantimos mais segurança no nosso módulo de usuários. Já podemos fechar ambos arquivos.
Em seguida, vamos acessar a pasta de "api > routes" para fazer a criação na nossa rota de usuários. Com o botão direito, escolhemos "Novo Arquivo" e o nomeamos como usuariosRoute.js
.
Primeiro, vamos importar a função Router
da biblioteca Express para a partir daí definir as nossas rotas e endpoints da aplicação. Para isso, digitamos const
seguido de Router
entre chaves que vai ser igual à require()
, passando express
entre aspas simples.
Em seguida, vamos criar uma instância de router
para facilitar na criação. Novamente, digitamos const router
, mas dessa vez em minúsculo para diferenciá-las. Isso vai ser igual a um Router()
com a primeira letra maiúscula, assim criamos uma instância dessa função Router()
.
usuariosRoute.js
:
const { Router } = require('express')
const router = Router()
Com isso, vamos conseguir criar todos os endpoints de usuário. Em uma nova linha, utilizamos router
minúsculo e um ponto. O editor já sugere alguns verbos do padrão HTTP do RESTful.
O primeiro endpoint que vamos utilizar será o post()
para criar o usuário. Para ficar mais organizado, saltamos uma linha, apertamos "Tab" e colocamos .post()
. Nele, vamos definir qual o endpoint que vai ser o path (caminho) que vamos adicionar para acessar essa rota. Nesse caso, /usuarios
entre aspas simples.
Em uma nova linha, podemos adicionar um .get()
que é um verbo para buscar. Também vamos passar o mesmo endpoint de /usuarios
.
Com isso, os dois endpoints têm o mesmo nome. Porém, como têm verbos diferentes, as interações também vão ser diferentes.
// códido omitido…
router
.post('/usuarios')
.get('/usuarios')
Abaixo de .get()
, podemos adicionar outro verbo .get()
, porém, com uma diferença do caminho de cima.
O primeiro busca /usuarios
, retornando todas as pessoas usuárias da aplicação. Porém, em alguns casos podemos precisar de apenas de uma pessoa usuária.
Por isso, nesse segundo .get()
, vamos fazer um endpoint de retorno de uma pessoa usuária a partir de um ID. Isto é, /usuarios/id/:id
entre aspas simples. Esse :id
significa que é um parâmetro do nosso endpoint. Quando consumimos esse endpoint, vamos passar um ID e recebê-lo no back-end.
Em seguida, vamos para a função de editar do tipo .put()
. Da mesma forma que temos a função de buscar apenas uma pessoa usuária pelo ID, o put()
também vai precisar de um ID. Pois, vamos editar apenas uma pessoa usuária por vez. Por isso, colocamos /usuarios/id/:id
entre aspas simples.
Por último, temos a função de deletar a pessoa usuária. Parecido a função de editar, a de deletar também vai precisar de um ID para saber qual pessoa deletar. Digitamos o verbo .delete()
com /usuarios/id/:id
entre aspas simples.
Agora que já temos todas as rotas e endpoints criados, vamos precisar exportá-las para importá-las no nosso arquivo index.js
do projeto.
Em uma nova linha, vamos exportar a variável router
, utilizando module.exports
igual à router
.
// códido omitido…
router
.post('/usuarios')
.get('/usuarios')
.get('/usuarios/id/:id')
.put('/usuarios/id/:id')
.delete('/usuarios/id/:id')
module.exports = router
Após exportar nossas rotas, vamos acessar o arquivo index.js
dentro de "src > routes". Verificamos que já importamos os produtos, pois já temos as rotas de produtos criadas.
Da mesma forma, abaixo de const produto
, vamos criar uma variável const
para importar o usuario
que vai ser igual à require()
, pegando usuariosRoute
do mesmo diretório.
Agora, podemos adicionar essa variável usuario
dentro da variável app
para poder utilizá-la. Então, após produto
, vamos digitar usuario
.
index.js
:
const bodyParser = require('body-parser')
const produto = require('./produtoRoute')
const usuario = require('./usuariosRoute')
module.exports = app => {
app.use(
bodyParser.json(),
produto,
usuario
)
}
Com isso, sabemos que nossas rotas de usuário funcionam. Agora, abrimos o terminal e digitamos o comando que definimos dentro do pack de JSON para rodar o projeto:
npm run start
servidor está rodando na porta 3000
Pronto. Nosso projeto está rodando sem mostrar nenhum erro.
Nesse vídeo, fizemos a criação da nossa tabela no banco de dados, criando a nossa migration e model. Também definimos um arquivo com todas as rotas de usario
e a importamos no index.js
.
No próximo vídeo, vamos dar início a parte da criação da primeira pessoa usuária.
Em vídeos anteriores, fizemos a criação de todas as rotas de usuário e a tabela no banco de dados. Com isso, podemos dar início a criação da primeira pessoa usuária.
Para isso, vamos fechar todos os arquivos que não utilizamos no VSCode. Em "api > controllers", vamos criar um novo arquivo usuarioController.js
.
Nele, vamos fazer a criação de uma classe da controller de usuário chamada UsuarioController
.
Já vamos exportar nossa controller para poder acessá-la em outros arquivos. Fora da classe, vamos dar um module.exports
igual à classe UsuarioController
.
usuarioController.js
:
class UsuarioController {
}
module.exports = UsuarioController
Agora que temos a controller de usuário criada, podemos ir ao diretório "src > services" e fazer a criação da service de usuário. Vamos criar um novo arquivo chamado usuarioService.js
.
Nele, também vamos criar uma classe chamada UsuarioService
para referenciar o service de usuário.
Da mesma forma, vamos fazer exportar essa classe para poder acessá-la em outros locais, como o controller. Para isso, escrevemos module.exports
igual à UsuarioService
.
usuarioService.js
:
class UsuarioService {
}
module.exports = UsuarioService
Com isso, podemos voltar ao arquivo usuarioController.js
e importar o service de usuário. Na primeira linha, vamos criar uma const
chamada UsuarioService
igual à require()
, onde
colocamos dois pontos para sair da pasta atual e ir para a pasta services
, onde temos o arquivo usuarioService
.
Agora podemos criar uma instância da service de usuário para poder acessar as suas funções internas. Em uma nova linha, criamos uma const
chamada usuarioService
com o "U" minúsculo para fazer a distinção da classe. Essa variável vai ser igual à new UsuarioService()
.
usuarioController.js
:
const UsuarioService = require('../services/usuarioService')
const usuarioService = new UsuarioService()
// código omitido…
Vamos voltar ao arquivo usuarioService.js
para criar nossa primeira função que vai cadastrar o usuário.
Dentro da classe UsuarioService
, vamos digitar async
para informar que o tipo da função é assíncrona para ser aguardada enquanto faz o cadastro.
Vamos dar o nome da função de cadastrar()
, recebendo um objeto chamado dto
. Dentro do DTO, vamos ter todas as informações do usuário, como nome, e-mail e senha. Em seguida, abrimos e fechamos chaves.
usuarioService.js
:
class UsuarioService {
async cadastrar(dto) {
}
}
// código omitido…
Vamos voltar novamente no arquivo usuarioController.js
para verificar se o import e a criação da classe estão funcionando.
Na classe UsuarioController
, vamos fazer a criação de uma função para cadastrar o usuário. Assim como na service, a função vai ser do tipo async
e ter o nome cadastrar()
.
Essa função de cadastro de usuários vai receber duas variáveis de entrada, uma vai ser a requisição que vai ter todos os dados do usuário e a outra vai ser a response que vamos retornar para quem solicitar a controller. Por isso, passamos req
e res
.
Entre as chaves da função cadastrar
, vamos fazer uma criação de variáveis via desestruturação para pegar as informações do usuário a partir da requisição do body.
Para isso, vamos dar um const
e colocar entre chaves: nome
, email
e senha
. Essas informações virão da req.body
. Com isso, temos todas as informações da requisição do usuário para poder cadastrá-lo.
Por isso, podemos acessar a função do service para cadastrar o usuário. Criamos a variável const
chamada usuario
igual à await
para esperar ser feito o cadastro e chamar a função usuarioService.cadastrar()
.
Perceba que o cadastrar()
recebe um dto
do tipo any
. Porém, vamos adicionar todas as informações do usuário. Por isso, vamos passar o objeto com nome
, email
e senha
do usuário.
Agora, podemos retornar o usuario
salvo. Vamos usar a função res
seguida de .status()
. Dentro do status, vamos passar um número que representa a ação que fizemos, ou seja, a requisição. Como estamos criando um usuário, vamos usar o status code 201
que significa created.
Após o status(201)
, vamos escrever .send()
para retornar as informações que queremos. No nosso caso, é o usuario
.
usuarioController.js
:
class UsuarioController {
async cadastrar(req, res) {
const { nome, email, senha } = req.body
const usuario = await usuarioService.cadastrar({ nome, email, senha})
res.status(201).send(usuario)
}
}
// código omitido…
Agora que recebemos os dados do usuário, enviamos para o back-end e retornamos da requisição, podemos acessar a service e receber esses dados.
Dentro de cadastrar()
em usuarioService.js
, vamos receber os dados do usuário. Porém, como vamos fazer um cadastro de um novo usuário, precisamos verificar se esse usuário já existe na base de dados.
Para fazer essa busca, vamos utilizar o e-mail do usuário, pois é único para cada usuário. Com isso, vamos evitar a duplicidade de cadastro. O nome pode se repetir, mas o e-mail pode ser a nossa chave primária.
Mas, ainda não temos acesso a nossa base de dados para poder acessar a tabela de usuários, pois não a importamos na service. Por isso, no começo do arquivo, vamos saltar uma linha e criamos const database
igual à require()
, saindo dessa pasta e acessando a pasta "models". Isto é, ../models
.
Com isso, já conseguimos acessar a base de dados. Pois, dentro de "models", temos um arquivo chamado index.js
que faz referência às models de produtos e usuários.
Agora, vamos continuar pela busca pelo usuário no service. Na função assíncrona cadastrar()
, criamos uma variável const
chamada usuario
, seguido do sinal de igual. Depois, vamos adicionar um await
e acessar a base de dados e a tabela de usuários. Isto é, database.usuarios
também vamos usar a função .findOne()
que recebe um objeto para passar filtros.
Entre as chaves, vamos utilizar o where
para definir qual vai ser o parâmetro de busca. Como dito anteriormente, vamos utilizar o email
, pegando da variável de entrada dto.email
.
A partir disso, já podemos ter o retorno de um usuário ou não. Por isso, vamos adicionar um if()
para verificar se esse usuário já é cadastrado. Caso já seja, vamos retornar um aviso para o usuário que não pode haver um cadastro duplicado.
Ainda em cadastrar()
, vamos adicionar um if()
, passando o usuario
como parâmetro. Um if
verdadeiro significa que o usuário está cadastrado. Por isso, utilizamos a função throw new Error()
para passar uma mensagem de erro ao usuário e parar a requisição. A mensagem que passamos é: "Usuario ja cadastrado".
usuarioService.js
:
const database = require('../models')
class UsuarioService {
async cadastrar(dto) {
const usuario = await database.usuarios.findOne({
where: {
email: dto.email
}
})
if (usuario) {
throw new Error('Usuario ja cadastrado')
}
}
// código omitido…
Com isso, conseguimos validar o cadastro do usuário. Porém, como damos um throw new Error()
e paramos a requisição, vamos precisar tratar o retorno na controller para evitar erros.
Em usuarioController.js
, vamos utilizar um padrão chamado try-catch
para tentar fazer algo ou retornar uma exceção.
Em cadastrar()
, abaixo das variáveis nome, email, senha
, vamos adicionar try
e escolher a sugestão de autocompletar "try-catch statement".
Vamos recortar as linhas de const usuario
e res.status(201)
com as informações da criação e retorno do usuário e colá-las dentro do try.
Em catch
, vamos adicionar um retorno para o usuário passando a mensagem de erro. Para isso, digitamos res.status()
com o status code 400
que sinaliza um problema na requisição. Também vamos acrescentar .send()
, passando o error.message
que passamos na service. Isto é, passamos um objeto chamado message
passando error.message
.
usuarioController.js
:
class UsuarioController {
async cadastrar(req, res) {
const { nome, email, senha } = req.body
try {
const usuario = await usuarioService.cadastrar({ nome, email, senha})
res.status(201).send(usuario)
} catch (error) {
res.status(400).send({ message: error.message})
}
}
}
// código omitido…
Dessa maneira, conseguimos validar que vamos o usuário vai receber uma mensagem de erro caso já esteja cadastrado. Além de já saber se está cadastrado pela service.
Com isso, finalizamos a primeira parte de buscar o usuário. No próximo vídeo, vamos terminar a parte de cadastro de usuário ao fazer a criptografia e salvar no banco de dados.
O curso Node.js: criando API Rest com autenticação, perfis de usuários e permissões possui 193 minutos de vídeos, em um total de 48 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.