Webscrapping com Node.js: Parte 2

Este artigo é uma continuação do Webscrapping com Node.js que escrevi no início de outubro. Crawlers e webscrappers são peças fundamentais para a construção de mecanismos de busca e já há algum tempo que estava para escrever sobre eles a pedido de leitores e amigos.

Isso porque crio tais bots há uns 7 anos, desde que comecei o projeto do Busca Acelerada pela primeira vez e de lá para cá não parei, tendo lançado diversos projetos para diversas empresas (como o BuildIn para a Softplan), e até mesmo alguns projetos pessoais (como o SóFamosos).

Mas por que uma parte 2? Não ensinei tudo que tinha de ensinar na parte 1?

O grande problema do webscrapping

Quase. O webscrapper que ensinei a criar na primeira parte deste artigo é bem básico e sofre de alguns males bem comuns que quem já deve ter feito webscrapping antes deve conhecer: HTML dinâmico e execução de scripts.

Claro, existem outros problemas também, como o bloqueio do bot após crawling excessivo, mas considerando que quase a totalidade dos sites atuais possuem execução de scripts para tornar suas páginas dinâmicas, é quase certo que você mais cedo ou mais tarde terá problemas com o seu webscrapper enxergando um HTML x e você no navegador enxergando um HTML y, o que dificultará seu trabalho de capturar os dados corretamente.

Explicando melhor: quando você acessa um site via servidor, usando um script como o webscrapper do post anterior, você não executa o código front-end da página, não manipula o DOM final, não dispara as chamadas Ajax, etc. O seu robô apenas lê as informações que foram geradas no servidor do site-alvo, o que pode fazer com que seu robô nem tenha nada de útil pra ler na página caso a mesma se construa usando Ajax.

A solução: Headless browsers

Para contornar este problema usamos um tipo especial de browser chamado headless (“sem cabeça”).

Os browsers web como conhecemos possuem 3 tarefas principais:

  • dada uma URL, acessar uma página HTML (funcionalidade principal, o tronco do browser);
  • carregar o DOM e executar os scripts front-end (os membros);
  • renderizar visualmente o DOM (a cabeça);

Os primeiros browsers antigos, que eram em modo texto, só faziam o primeiro item. O browser que você está usando pra ler este post faz os três. Um headless browser faz somente os dois primeiros, ou seja, ele apenas não possui uma interface gráfica, mas carrega o HTML, o DOM e executa todos os scripts de front-end.

Esse tipo de browser é como se fosse um console application rodando em modo silencioso, apenas um processo no seu sistema operacional que age como um browser. No fundo ele é um browser, apenas não tem UI.

Muito útil para testes automatizados de interface, é comum os headless browsers exporem bibliotecas de manipulação do DOM como JQuery para permitir que através de scripts o programador consiga usar a página carregada pelo headless browser, como executar cliques em botões, preencher campos, etc; tudo isso sem estar enxergando uma UI ou sequer estar usando mouse e teclado.

Não é preciso ser um gênio para entender que podemos usar headless browsers para fazer webscrapping, certo? Com esse tipo de tecnologia conseguiremos que nosso bot leia a versão final do HTML, da mesma forma que um usuário “veria” no navegador, após a execução dos scripts de front-end. Não apenas isso, mas você consegue fazer com que seu bot não apenas leia conteúdo, mas que use a página em si, clicando em botões e selecionando opções, por exemplo.

Se DOM, HTML, front-end e outros termos utilizados aqui não forem de seu domínio, dificilmente você conseguirá criar um crawler realmente bom. Em meu livro de Programação Web com Node.js, cubro estes e outros aspectos essenciais da programação web, caso te interesse.

Escolhendo um headless browser

Existem diversas opções de headless browsers no mercado. A maioria é open-source e baseada no Webkit, a mesma base na qual o Apple Safari e o Google Chrome foram construídos, o que garante uma grande fidelidade em relação ao uso de web browser comuns para carregar o HTML+JS.

Algumas opções disponíveis são:

Destes, o SlimerJS usa a plataforma Gecko da Mozilla ao invés do Webkit, enquanto que o PhantomJS e o HtmlUnit são famosos por se integrarem ao Selenium, uma popular ferramenta de automação de testes. Além disso, o Phantom é o único que eu usei profissionalmente em uma startup que trabalhei que usava este headless browser para tirar screenshots de páginas web.

No entanto, meu intuito de escrever este post era justamente para aprender a usar outro headless browser que não o Phantom, pois este possui alguns problemas conhecidos como não-conformidade com ES6, obrigatoriedade de uso do JQuery e nenhuma novidade desde 2016 (o projeto está parado). Esses problemas (que não são o fim do mundo, mas que me motivaram a buscar outra tecnologia) são comentados neste excelente post do Matheus Rossi. Então se você realmente quer aprender a usar o PhantomJS, leia o post que acabei de citar.

Uma coisa que descobri recentemente é que desde a versão 59 do navegador Google Chrome que foi disponibilizada a opção de rodar o navegador do Google sem a interface gráfica funcionando, em modo headless. Usar o Chrome em modo headless é o sonho de muito testador por aí, pois representa rodar scripts de testes automatizados no navegador mais popular do mundo, com a maior fidelidade possível em relação ao que o usuário final vai usar.

Certo, headless browser escolhido, como fazer um webscrapper que use-o para carregar o mesmo DOM que um usuário do Chrome veria?

Manipulando o headless browser via Node.js

No mesmo Github do Google Chrome existe um projeto chamado Puppeteer, que em uma tradução livre seria um manipulador de marionetes, ou um mestre dos fantoches. Ele é essencialmente um módulo Node.js que permite manipular tanto o Headless Chrome quanto o Chrome “normal” para fazer tarefas como:

  • gerar screenshots e PDFs de páginas web;
  • fazer crawling em cima de páginas com Ajax e SPAs;
  • automatizar o uso de UIs web;
  • fazer webscrapping com alta fidelidade;

Para que o Puppeteer funcione você precisa ter o Node.js versão 8+ instalado na sua máquina. Depois que tiver o Node instalado, vamos criar um novo projeto Node.js chamado webscrapper2, criar um index.js vazio dentro dele, navegar via terminal até essa pasta e rodar um ‘npm init’ para configurá-lo com as opções default.

Se você nunca programou em Node.js antes, sugiro começar pelos tutoriais mais iniciantes de meu blog ou mesmo pelo meu livro, que pega o iniciante pela mão e ensina desde JS tradicional até diversos módulos do Node.

Agora rode o comando abaixo para instalar o Puppeteer no seu projeto, sendo que ele automaticamente irá baixar sua própria versão do Chrome, independente se você já possuir uma (ou seja, a instalação irá demorar um pouco mais que o normal para módulos NPM):

Usá-lo é muito simples, no seu index.js, inclua o seguinte código que representa a estrutura básica do nosso webscrapper:

Na primeira linha carregamos a dependência do módulo puppeteer. No bloco seguinte, criamos a função assíncrona que fará todo o trabalho de verdade, com a lógica de acesso à página e scrapping (comentado).

Neste primeiro momento, essa função scrape apenas executa o Headless Chrome, acessa uma nova página com a URL de um site fake de livros e espera 1 segundo antes de se fechar. Ainda não há a lógica de scrapping aqui.

No bloco final, executamos a função e quando ela terminar, o valor retornado pela função scrape será impresso no console.

Antes de fazer a versão completa da função scrape, temos de entender o que vamos capturar de informações da página em questão.

Fazendo HTML Scrapping

O site Books to Scrape serve para o único propósito de ajudar desenvolvedores a praticar webscrapping. Ele mostra um catálogo de livros, como na imagem abaixo:

Books to Scrape
Books to Scrape

Acesse ele no seu navegador Chrome e com o clique direito do mouse sobre a área do primeiro livro da listagem, mais especificamente sobre o título dele e selecione a opção Inspecionar/Inspect.

Isso irá abrir a aba Elements do Google Developer Tools onde você poderá entender como construir um seletor que diga ao seu web scrapper onde estão os dados que ele deve ler. Neste caso, vamos ler apenas os nomes dos livros. Se você não está acostumado com CSS e/ou seletores (no melhor estilo JQuery), você pode usar o recurso Copy > Selector que aparece no painel elements para gerar o seletor para você, como mostra a imagem abaixo.

Copiando o selector
Copiando o selector

Nesse caso específico não usarei a sugestão do Chrome que é bem engessada. Usarei um seletor bem mais simples: “h3 > a”, ou seja, vou pegar o link/âncora de cada H3 do HTML dessa página. Os H3 dessa página são exatamente os títulos dos livros.

Se analisarmos em detalhes este bloco que queremos extrair o título, veremos que o texto interno da âncora nem sempre é o título fiel pois, por uma questão de espaço, algumas vezes ele está truncado com reticências no final. Sendo assim, o atributo HTML que vamos ler com o scraper é o title da âncora, que esse sim sempre possui o título completo.

Vamos atualizar nosso código de scrapping para pegarmos esta informação:

Analisando apenas o bloco de código de scrapping, criamos uma função que executa um código JavaScript sobre a página que carregamos. A execução de scripts é realizada usando a função evaluate. Nesse código JavaScript, estamos usando a função querySelector no documento e o seletor que criamos anteriormente para acessar um componente do DOM da página. Sobre este componente, queremos o seu atributo title.

Se você rodar este código Node agora verá que ele retornará apenas o nome do primeiro livro, “A Light in the Attic”. Isso porque a função querySelector retorna apenas o primeiro componente que ela encontra com aquele seletor.

O código abaixo resolve isso:

Aqui, usei a querySelectorAll, sobre a qual apliquei um forEach. Para cada livro encontrado com aquele seletor, eu adiciono o title do livro em um array de livros, retornado ao fim do evaluate.

Com isso, se você executar novamente este script verá que ele retorna o título de todos os livros da primeira página do site, concluindo este post.

Qual seria o próximo passo? Navegar pelas demais páginas, coletando os títulos dos outros livros. Esta é a tarefa principal de um webcrawler ou webspider, to crawl (rastejar). Ao algoritmo de webscrapping cabe apenas coletar os dados.

Até a próxima!

Curtiu o post? Que tal aprender mais sobre como criar aplicações incríveis usando Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Webscrapping com Node.js

Eu crio webcrawlers e mecanismos de busca tem 7 anos na data que escrevo este post, sendo um dos empreendimentos digitais mais bem sucedidos da minha carreira o Busca Acelerada, um buscador de classificados automotivos. Já falei de como você pode criar buscadores em outras oportunidades que listo abaixo:

No entanto, eu nunca havia escrito antes sobre como criar um webcrawler antes, ou seja, um script que automaticamente percorre páginas web em busca de informações a serem indexadas para seu buscador. Na verdade eu não vou mostrar hoje como criar um webcrawler completo (que é uma tarefa que une muitos conceitos para um post só), mas sim como fazer um algoritmo que coleta informações de páginas HTML e armazena em um banco de dados, uma técnica chamada webscrapping.

Para que você consiga acompanhar este tutorial é importante que já esteja familiarizado com Node.js e MongoDB, que trato bastante aqui no blog em posts como esse e em meu livro sobre o assunto.

Veremos neste post:

  1. Utilidade do webscrapping
  2. Criando o projeto
  3. Programando o webscrapper
  4. Trabalhando com os dados

Vamos lá!

#1 – Utilidade do Webscrapping

Basicamente webscrapping consiste em ler o conteúdo HTML de páginas web, extrair as informações que você deseja se baseando em padrões de elementos (tags), armazenar no seu banco de dados e ignorar o resto, avançando para a próxima página.

Webscrapping é útil em diversos cenários e largamente utilizado por grandes empresas de tecnologia e inteligência competitiva.

  • você pode fazer scrapping de redes sociais para descobrir tópicos que estão na moda, como o Sentimonitor faz
  • você pode fazer scrapping de endereços de email disponíveis em websites para vender como o Hunter.io faz
  • você pode fazer scrapping de informações de outros sites para usar no seu, como o Google faz
  • você pode fazer scrapping de preços de produtos em ecommerces para criar comparadores, como o Buscapé faz

Note, no entanto, que muitos websites consideram webscrapping errado, violando seus termos de uso. Sendo assim, utilize as técnicas apresentadas neste artigo com cuidado pois caso o seu webcrawler fique visitando demais o mesmo site é bem possível que seu IP seja bloqueado ou até mesmo que você receba emails com ameaças.

Faça webscrapping por sua própria conta e risco.

#2 – Criando o projeto

Agora que sabe o que é e para que serve webscrapping, vamos criar este projeto.

Crie uma pasta para salvar os fontes deste projeto na sua máquina. Eu chamei a minha de webscrapper.

Entre na pasta webscrapper via terminal e rode o seguinte comando:

Siga o passo-a-passo que irá aparecer para criar as configurações desse projeto Node.js. Depois disso, vamos instalar as dependências que precisaremos neste projeto, usando o comando abaixo:

“npm i” significa “NPM Install”, o “-S” indica que as dependências devem ser salvas no seu packages.json e os demais nomes são os pacotes que devem ser instalados:

  • request: biblioteca para fazer requisições HTTP
  • request-promise: oferece suporte a promises com request (uma nova forma de lidar com os callbacks do ES7)
  • cheerio: oferece as funcionalidades de seletores do JQuery em Node.js

Agora crie um arquivo index.js na raiz do seu projeto e vamos seguir em frente pois o setup está pronto!

Ah, sugiro usar o Visual Studio Code para ser mais produtivo programando em Node.js. É gratuito e multiplataforma.

#3 – Programando o Webscrapper

Comece o seu index.js adicionando as referências às bibliotecas que instalamos anteriormente:

A biblioteca request-promise aceita um objeto como input de suas requests e retorna uma promise. Basicamente este objeto, que chamarei de options, precisa de duas coisas:

  1. a URL que vamos fazer scrapping
  2. a função de transformação com o HTML retornado da página

Nesta função de transformação vamos dizer ao request-promise para delegar ao cheerio o carregamento do HTML. O código abaixo demonstra um objeto options válido, apontando para a página com os resultados do Brasileirão Série A do Globo Esporte:

A ideia aqui é pegar as informações da tabela do Brasileirão diretamente do site do Globo Esporte, provavelmente o mais atualizado do país neste sentido. O que vou fazer com esses dados? Eu não tenho nem ideia, mas achei que seria um bom exemplo…

Agora para fazer a requisição é bem simples, basta usarmos o objeto rp como uma função passando o objeto options para ele por parâmetro. Como é uma função que retorna uma promise JS, usaremos then e catch para lidar com o callback de retorno e com um possível erro, respectivamente:

Se você seguiu os passos corretos até agora, abra o terminal e na pasta do projeto webscrapper digite o seguinte comando para executar este arquivo:

Como resultado, você deve ver no terminal uma mensagem semelhante à essa, indicando que o HTML de retorno do site foi carregado com sucesso:

Isso quer dizer que seu webscrapper está funcionando!

#4 – Trabalhando com os dados

Agora que você tem um webscrapper simples porém funcional, é hora de trabalhar com aquele objeto que representa o documento HTML retornado pelo request-promise e transformado pelo cheerio.

Se você nunca usou JQuery antes (no meu livro de Node.js é um dos tópicos de front-end), o mais importante aqui é bem simples de aprender: seletores. Basicamente todo seletor começa com o cifrão ($) seguido de parênteses e o identificador do seletor que pode ser:

  • #idDoElemento
  • .classeDoElemento
  • tagDoElemento
  • tag[atributo=valor]

E muito mais, mas esses aí são os principais. Ou seja, se quiser carregar em memória um elemento HTML cujo id seja divCadastro, em JQuery você faria o seguinte:

E depois na sequência poderia chamar funções para pegar informações deste elemento, incluindo seu texto, seu HTML, seus atributos, seus nós-filhos, etc.

E cheerio trabalha exatamente desta forma!

Para entender a informação que vamos pegar, é sempre útil ver o código-fonte da página que estamos fazendo scrapping ou melhor ainda: usar o F12 do Google Chrome para inspecionar elementos específicos, como fiz na imagem abaixo para entender a estrutura HTML da tabela do brasileirão.

HTML do Brasileirão
HTML do Brasileirão

Note que todas as linhas com nomes de clubes (tr) possuem a classe ‘tabela-body-linha’, sendo assim, fica fácil de fazer um seletor por essa classe e com um laço descobrir todas as linhas. Dentro de cada linha, usamos a função find para achar os elementos que possuem a classe com o nome de cada um dos times (tabela-times-time-nome):

Se você executar agora, verá no console o nome de todos os times da série A do Brasileirão (desculpe Internacional/RS, mas você não está na lista XD ).

Para encerrar, vou modificar o código uma última vez e colocá-lo todo abaixo, para pegar além do nome do time, a posição atual dele na tabela, guardando estas informações em um array JSON que facilmente você poderia depois salvar em um banco de dados como o MongoDB, como já mostrei em outros posts.

Adicionei alguns códigos adicionais para incluir uma função de processamento, proporcionando um ponto onde facilmente você pode colocar sua lógica de persistência de dados. Também coloquei um teste para evitar salvar dados em branco (algo que com um pouco mais de paciência pode ser melhor filtrado com o cheerio).

O resultado esperado deste simples webscrapper, na data que escrevo este post é o array JSON abaixo impresso no console:

Atente ao fato de que muitos sites alteram o DOM da página usando JavaScript e que algumas vezes você não conseguirá ter acesso aos elementos HTML via cheerio. Nestes casos a técnica correta envolve usar headless browsers como o Phantom.

Mas essa é uma técnica muito mais elaborada que explico neste outro post.

Espero que tenha gostado!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Autenticação em Node.js com Passport – Parte 2

Este post é uma continuação do tutorial de como implantar um mecanismo de autenticação completo em Node.js usando o middleware Passport. No tutorial passado fizemos funcionar a autenticação através de uma tela de login e impedimos que usuários anônimos entrem na tela de chat.

Nesta segunda parte vamos criar a tela de cadastro de usuário (Sign Up) e a tela de recuperação de senha (Forget Password), além que aprenderemos a enviar emails em Node.js.

  1. Criando o cadastro de usuário
  2. Aprendendo a enviar emails
  3. Criando a recuperação de senha

Vamos lá!

#1 – Criando o cadastro de usuário

No tutorial anterior deixamos nosso banco preparado e a conexão compartilhada em uma variável global.db. Usaremos esta conexão global para fazer as operações de banco necessárias. Também usaremos o mesmo módulo bcrypt para criptografar as senhas no banco.

Para começar, vamos criar a nossa tela de cadastro de usuário que pedirá username, password e email, chamei ela de views/signup.ejs:

Note que este form faz um POST para uma rota /users/signup e que ele já espera uma message no model para exibir erro, caso dê algum problema. Faremos isso tudo funcionar depois.

Para que essa view possa ser exibida, primeiro vamos criar um novo arquivo de rotas em routes/users.js. Aqui colocaremos todas as rotas relacionadas a usuários, a começar pela rota que exibe a view que acabamos de criar via GET:

Note que já fiz o teste para passar ou não uma mensagem de falha para a tela no model, pra não dar erro na minha view. Agora para que essa rota passe a funcionar, temos de configurar nosso app.js, como abaixo:

Salve tudo e mande rodar sua aplicação para ver ela funcionando no navegador (use o link de “Não possui cadastro?” que deixamos prontos na tela de login):

Cadastro de Usuário
Cadastro de Usuário

Note que para acessar essa tela a rota é /users/signup. Isso porque definimos no app.js que rotas ‘/’ são tratadas pela index.js e rotas ‘/users’ são tratadas pela users.js, ou seja, há um roteamento inicial com ‘/users’ e depois o outro roteamento com ‘/signup’, virando ‘/users/signup’ a rota completa.

Agora, antes de sair programando a rota POST /users/signup que está em nosso HTML FORM, devemos criar a função JS que vai salvar um novo usuário no MongoDB. Para armazenar não apenas essa mas outras funções relacionadas a banco de dados (que não são de autenticação, pois estas deixamos no arquivo auth.js), vamos criar um arquivo db.js na raiz do nosso projeto. Dentro dele, vamos criar e expor uma função createUser, como abaixo:

Aqui eu pego a senha plain-text enviada pela view e gero o hash dela usando um salt de 10 rounds, que foi o que achei como sendo seguro atualmente, embora esta resposta dê um panorama mais completo de como calcular isso se seu sistema realmente precisar de uma segurança acima da média (bancos?). Ao final do processo, chamo uma função de callback que a createUser espera como último parâmetro.

Com esse módulo db.js pronto, programar essa rota de /users/signup será bem simples! Vá em routes/users.js e adicione uma rota POST para /signup, como abaixo (lembrando que o path /users já é tratado no app.js):

Aqui eu carrego o módulo ‘db’ que acabamos de criar e com ele chamo a função createUser, passando os dados enviados no corpo da requisição HTTP: username, password e email. Como último parâmetro, passo uma arrow function como callback, que apenas vai redirecionar para a tela de login em caso de sucesso e para a própria tela de signup em caso de fracasso, onde uma mensagem de erro será exibida.

Execute novamente sua aplicação e teste a criação de usuário. Se tudo deu certo, após a criação você poderá se autenticar na tela de login usando esse novo usuário.

Mas o que você acha de mandarmos um email de boas vindas para esse usuário que recém se cadastrou?


#2 – Aprendendo a enviar emails

Após o usuário se cadastrar, vamos enviar um email de boas vindas para ele. Além de ser algo interessante, fazer a lógica de envio de emails será útil na próxima etapa do tutorial, que é reset de senha por email.

Para fazer o envio de emails vamos usar um módulo chamado Nodemailer, que deve ser instalado via linha de comando usando o NPM:

Depois, vamos criar um módulo mail.js na raiz do nosso projeto, com o seguinte código:

Nosso módulo mail.js apenas expõe uma função que espera o destinatário da mensagem (to), o assunto (subject) e o texto (text). Por fim, a função sendMail faz o serviço propriamente dito.

Note que as configurações de SMTP para envio de email estão definidas em variáveis de ambiente, assim como fizemos na parte 1 deste tutorial para guardar a connection string do MongoDB.

Não esqueça que para que essas variáveis de ambiente funcionem, você deve adicionar o modelo delas no arquivo .env.example (é um arquivo oculto, você só vai conseguir ver ele com VS Code ou no terminal):

E com os valores finais no arquivo .env:

Aqui usei minha conta de email na Umbler para enviar os emails (você pode criar sua conta lá e ganhar créditos de graça para gastar com um email que custa centavos por mês). Como é uma aplicação de teste que vai enviar poucos emails, não vamos ter problemas. No entanto, caso queira usar o nodemailer para enviar muitos emails, você rapidamente será bloqueado em qualquer provedor de hospedagem. O certo é usar serviços de SMTP Gateway como Amazon SES, Mandrill e SendGrid.

Agora, para usarmos este módulo e finalmente fazer o envio de email de boas vindas funcionar, adicione apenas uma linha a mais em nossa rota POST em users.js:

Obviamente você pode personalizar essa mensagem livremente, ao invés do “email espartano” que deixei configurado. Soluções mais profissionais geralmente envolvem criar templates HTML que são lidos usando o módulo ‘fs’, algumas variáveis dentro do template são trocadas e depois a string resultante é enviada como HTML.

Rode novamente sua aplicação com ‘npm start’ e realize o cadastro de um novo usuário, mas informando um email válido, para que a mensagem chegue e você possa ver o resultado no seu webmail.

Email enviado via Node.js
Email enviado via Node.js

#3 – Criando a recuperação de senha

Agora a próxima parte é a de recuperação de senha. Lembra que deixamos um link pra isso lá na tela de login?

Vamos começar criando uma nova view, a views/forgot.ejs, com um formulário bem simples que pede a confirmação de seu email para envio de uma nova senha:

E para que essa tela seja acessível, vamos criar uma nova rota GET em routes/users.js:

Rodando sua aplicação e acessando no navegador, já deve ser possível navegar até esta página usando o link na tela de login:

Esqueceu a senha
Esqueceu a senha

Para fazer funcionar este formulário, vamos começar criando duas funções em nosso arquivo db.js: uma que busca um usuário pelo seu email e outra que muda a senha de um usuário:

A primeira função é bem simples, um findOne por email, que executa um callback após encontrar (ou não) o usuário. A segunda recebe uma senha, criptografa ela e sobrescreve o hash de senha do usuário cujo id foi passado por parâmetro. O module.exports no final do db.js também foi atualizado de acordo.

Note que não estou criando as devidas validações em nenhum formulário para os artigos não ficarem gigantescos. Nem de campos obrigatórios, nem validações de regras de negócio como não permitir usuários e emails duplicados. Tenha em mente que você deverá implementar essas questões se for usar esses códigos em produção.

Antes de sairmos fazendo a rota POST, vamos criar um arquivo utils.js na raiz do nosso projeto e colocar dentro dele uma função de geração de senha aleatória:

Esta função é bem simples e talvez você até queira usar alguma mais avançada. Aqui eu crio e retorno uma senha aleatória de 10 caracteres alfanuméricos (maiúsculas, minúsculas e números).

Agora que temos estas funções podemos criar a nossa rota POST que vai receber os dados do formulário de “esqueci minha senha”. Abra o arquivo routes/users.js e crie a nova rota:

Aqui começamos com o findUser que criamos anteriormente, que busca usuário por email. Caso não encontre, vamos jogar o usuário para a tela de login mesmo assim, embora você possa pensar em algo mais criativo para fazer.

Caso encontre, mandamos gerar uma nova senha usando a função que criei há pouco e uso o changePassword para mudar a senha do usuário que possui o email especificado (em um sistema de produção, para evitar problemas, além do email, peça alguma informação pessoal do usuário para confirmar esse processo de troca de senha). Por fim, um email é enviado com a senha que acabou de ser gerada, como na imagem abaixo.

Nova senha
Nova senha

Se você tentar se autenticar com a senha antiga, notará que ela não funciona mais, somente a nova.

É uma boa prática guardar o hash das últimas senhas do usuário e não deixar que ele use senhas antigas (mesmo senhas aleatórias podem conflitar ocasionalmente). Outra boa prática seria no primeiro login com a nova senha ele ser solicitado a cadastrar uma senha pessoal ao invés dessa aleatória. Enfim, sempre há margem para melhorar quando o assunto é segurança.

Pretendo em uma futura parte 3, abordar outras questões relacionadas a segurança, como autorização (visto que até o momento vimos apenas autenticação).

Até a próxima!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!