Implementar estratégias de autenticação robustas para qualquer aplicativo pode ser uma tarefa assustadora e os aplicativos Node.js não são exceção a isso.
Neste tutorial, desenvolveremos um aplicativo Node.js do zero e usaremos um middleware de autenticação relativamente novo, mas muito popular – o Passport para cuidar de nossas preocupações de autenticação.
A documentação do Passport o descreve como um “middleware de autenticação simples e discreto para Node” e com razão.
Ao fornecer a si mesmo como um middleware, o Passport faz um excelente trabalho ao separar as outras preocupações de um aplicativo da Web de suas necessidades de autenticação. Ele permite que o Passport seja facilmente configurado em qualquer aplicativo da Web baseado em Express, assim como configuramos outro middleware Express, como log, análise de corpo, análise de cookie, manipulação de sessão etc.
Este tutorial pressupõe uma compreensão básica do Node.js e da estrutura Express e tenta manter o foco na autenticação, embora criemos um aplicativo Express de exemplo do zero e progredimos adicionando rotas a ele e autenticando algumas dessas rotas.
Se você precisar de ajuda com qualquer coisa, desde depuração até novos recursos, tente trabalhar com alguns dos desenvolvedores JavaScript experientes no Envato Studio.
Estratégias de autenticação
O Passport nos fornece mais de 140 mecanismos de autenticação para escolher. Você pode autenticar em uma instância de banco de dados local/remota ou usar o logon único usando provedores OAuth para Facebook, Twitter, Google etc. com o Passport e forneça um módulo de nó para isso.
Mas não se preocupe: você não precisa incluir nenhuma estratégia/mecanismo que seu aplicativo não precise. Todas essas estratégias são independentes umas das outras e empacotadas como módulos de nó separados que não são incluídos por padrão quando você instala o middleware do Passport: npm install passport
Neste tutorial, usaremos a Estratégia de Autenticação Local do Passport e autenticaremos os usuários em uma instância do Mongo DB configurada localmente, armazenando os detalhes do usuário no banco de dados. Para usar a Estratégia de Autenticação Local, precisamos instalar o módulo local do passaporte: npm install passport-local
Mas espere: antes de iniciar seu terminal e começar a executar esses comandos, vamos começar construindo um aplicativo Express do zero e adicionar algumas rotas a ele (para login, registro e home) e depois tentar adicionar nosso middleware de autenticação a ele. Observe que usaremos o Express 4 para os propósitos deste tutorial, mas com algumas pequenas diferenças, o Passport também funciona bem com o Express 3.
Configurando o aplicativo
Se você ainda não o fez, vá em frente e instale o Express & express-generator para gerar um aplicativo clichê simplesmente executando express passport-mongo
no terminal. A estrutura do aplicativo gerado deve ficar assim:
Vamos remover algumas das funcionalidades padrão que não usaremos – vá em frente e exclua o users.js
rota e remova suas referências do app.js
Arquivo.
Adicionando dependências do projeto
Abra package.json
e adicione as dependências para passport
e passport-local
módulo.
"passport": "~0.2.0", "passport-local": "~1.0.0"
Como salvaremos os detalhes do usuário no MongoDB, usaremos o Mongoose como nossa ferramenta de modelagem de dados de objetos. Outra maneira de instalar e salvar a dependência para package.json
é digitando:
npm install mongoose --save
package.json
deve ficar assim:
Agora, instale todas as dependências e execute o aplicativo clichê executando npm install && npm start
. Ele agora baixará e instalará todas as dependências e iniciará o servidor do nó. Você pode verificar o aplicativo Express básico em http://localhost:3000/, mas não há muito o que ver.
Muito em breve, vamos mudar isso criando um aplicativo expresso completo que pede para mostrar uma página de registro para um novo usuário, o login de um usuário registrado e autenticar o usuário registrado usando o Passport.
Criando Modelo Mangusto
Como estaremos salvando os detalhes do usuário no Mongo, vamos criar um modelo de usuário no Mongoose e salvá-lo em models/user.js
em nosso aplicativo.
var mongoose = require('mongoose'); module.exports = mongoose.model('User',{ username: String, password: String, email: String, gender: String, address: String });
Basicamente, estamos criando um modelo Mongoose usando o qual podemos realizar operações CRUD no banco de dados subjacente.
Configurando o Mongo
Se você não tiver o Mongo instalado localmente, recomendamos que você use serviços de banco de dados em nuvem, como Modulus ou MongoLab. Criar uma instância funcional do MongoDB usando isso não é apenas gratuito, mas é apenas uma questão de alguns cliques.
Depois de criar um banco de dados em um desses serviços, ele fornecerá um URI de banco de dados comomongodb://
que pode ser usado para executar operações CRUD no banco de dados. É uma boa ideia manter a configuração do banco de dados em um arquivo separado que pode ser acessado quando necessário. Como tal, criamos um módulo de nó db.js
que se parece com:
module.exports = { 'url' : 'mongodb://: @novus.modulusmongo.net:27017/ ' }
Se você é como eu, está usando uma instância local do Mongo, então é hora de iniciar o mongod
demônio e o db.js
deve parecer
module.exports = { 'url' : 'mongodb://localhost/passport' }
Agora usamos essa configuração em app.js
e conecte-se a ele usando APIs do Mongoose:
var dbConfig = require('./db.js'); var mongoose = require('mongoose'); mongoose.connect(dbConfig.url);
Configurando o Passport
O Passport apenas fornece o mecanismo para lidar com a autenticação, deixando o ônus de implementar o tratamento de sessão e para isso usaremos a sessão expressa. Abra app.js
e cole o código abaixo antes de configurar as rotas:
// Configuring Passport var passport = require('passport'); var expressSession = require('express-session'); app.use(expressSession({secret: 'mySecretKey'})); app.use(passport.initialize()); app.use(passport.session());
Isso é necessário, pois queremos que nossas sessões de usuário sejam persistentes por natureza. Antes de executar o aplicativo, precisamos instalar a sessão expressa e adicioná-la à nossa lista de dependências em package.json
. Para fazer esse tipo npm install --save express-session
Serializando e desserializando instâncias de usuário
O Passport também precisa serializar e desserializar a instância do usuário de um armazenamento de sessão para oferecer suporte a sessões de login, para que cada solicitação subsequente não contenha as credenciais do usuário. Ele fornece dois métodos serializeUser
e deserializeUser
para o efeito:
passport.serializeUser(function(user, done) { done(null, user._id); }); passport.deserializeUser(function(id, done) { User.findById(id, function(err, user) { done(err, user); }); });
Usando estratégias de passaporte
Vamos agora definir as estratégias do Passport para lidar com Conecte-se e inscrever-se. Cada um deles seria uma instância do Estratégia de autenticação local de Passaporte e seria criado usando o passport.use()
função. Usamos o connect-flash para nos ajudar no tratamento de erros, fornecendo mensagens em flash que podem ser exibidas ao usuário em caso de erro.
Estratégia de login
A estratégia de login é assim:
// passport/login.js passport.use('login', new LocalStrategy({ passReqToCallback : true }, function(req, username, password, done) { // check in mongo if a user with username exists or not User.findOne({ 'username' : username }, function(err, user) { // In case of any error, return using the done method if (err) return done(err); // Username does not exist, log error & redirect back if (!user){ console.log('User Not Found with username '+username); return done(null, false, req.flash('message', 'User Not found.')); } // User exists but wrong password, log the error if (!isValidPassword(user, password)){ console.log('Invalid Password'); return done(null, false, req.flash('message', 'Invalid Password')); } // User and password both match, return user from // done method which will be treated like success return done(null, user); } ); }));
O primeiro parâmetro a passport.use()
é o nome da estratégia que será usada para identificar esta estratégia quando aplicada mais tarde. O segundo parâmetro é o modelo de estratégia que você deseja criar, aqui usamos o nome de usuário-senha ou o LocalStrategy. Deve-se notar que, por padrão, o LocalStrategy espera encontrar as credenciais do usuário em username
& password
parâmetros, mas também nos permite usar quaisquer outros parâmetros nomeados. o passReqToCallback
variável de configuração nos permite acessar o request
objeto no retorno de chamada, permitindo-nos usar qualquer parâmetro associado à solicitação.
Em seguida, usamos a API do Mongoose para encontrar o usuário em nossa coleção subjacente de usuários para verificar se o usuário é um usuário válido ou não. O último parâmetro em nosso retorno de chamada: done
denota um método útil com o qual podemos sinalizar sucesso ou falha no módulo Passport. Para especificar a falha, o primeiro parâmetro deve conter o erro ou o segundo parâmetro deve avaliar para false
. Para significar sucesso, o primeiro parâmetro deve ser null
e o segundo parâmetro deve avaliar para um truthy
valor, caso em que será disponibilizado no request
objeto
Como as senhas são inerentemente fracas por natureza, devemos sempre criptografá-las antes de salvá-las no banco de dados. Para isso, usamos bcrypt-nodejs para nos ajudar com criptografia e descriptografia de senhas.
var isValidPassword = function(user, password){ return bCrypt.compareSync(password, user.password); }
Se você está se sentindo desconfortável com os trechos de código e prefere ver o código completo em ação, sinta-se à vontade para navegar pelo código aqui.
Estratégia de registro
Agora, definimos a próxima estratégia que tratará do registro de um novo usuário e criará sua entrada em nosso Mongo DB subjacente:
passport.use('signup', new LocalStrategy({ passReqToCallback : true }, function(req, username, password, done) { findOrCreateUser = function(){ // find a user in Mongo with provided username User.findOne({'username':username},function(err, user) { // In case of any error return if (err){ console.log('Error in SignUp: '+err); return done(err); } // already exists if (user) { console.log('User already exists'); return done(null, false, req.flash('message','User Already Exists')); } else { // if there is no user with that email // create the user var newUser = new User(); // set the user's local credentials newUser.username = username; newUser.password = createHash(password); newUser.email = req.param('email'); newUser.firstName = req.param('firstName'); newUser.lastName = req.param('lastName'); // save the user newUser.save(function(err) { if (err){ console.log('Error in Saving user: '+err); throw err; } console.log('User Registration succesful'); return done(null, newUser); }); } }); }; // Delay the execution of findOrCreateUser and execute // the method in the next tick of the event loop process.nextTick(findOrCreateUser); }); );
Aqui, novamente usamos a API do Mongoose para descobrir se algum usuário com o nome de usuário fornecido já existe ou não. Caso contrário, crie um novo usuário e salve as informações do usuário no Mongo. Caso contrário, retorne o erro usando o done
mensagens de retorno de chamada e flash. Observe que usamos bcrypt-nodejs
para criar o hash da senha antes de salvá-la:
// Generates hash using bCrypt var createHash = function(password){ return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null); }
Criando Rotas
Se tivéssemos uma visão panorâmica do nosso aplicativo, ficaria assim:
Agora definimos nossas rotas para o aplicativo no módulo a seguir que recebe a instância do Passport criada em app.js
acima de. Salve este módulo em routes/index.js
module.exports = function(passport){ /* GET login page. */ router.get('/', function(req, res) { // Display the Login page with any flash message, if any res.render('index', { message: req.flash('message') }); }); /* Handle Login POST */ router.post('/login', passport.authenticate('login', { successRedirect: '/home', failureRedirect: '/', failureFlash : true })); /* GET Registration Page */ router.get('/signup', function(req, res){ res.render('register',{message: req.flash('message')}); }); /* Handle Registration POST */ router.post('/signup', passport.authenticate('signup', { successRedirect: '/home', failureRedirect: '/signup', failureFlash : true })); return router; }
A parte mais importante do trecho de código acima é o uso de passport.authenticate()
delegar a autenticação para login
e signup
estratégias quando um HTTP POST
é feito para /login
e /signup
rotas respectivamente. Observe que não é obrigatório nomear as estratégias no caminho da rota e pode ser qualquer nome.
Criando Vistas de Jade
Em seguida, criamos as duas visualizações a seguir para nosso aplicativo:
-
layout.jade
contém as informações básicas de layout e estilo -
index.jade
contém a página de login contendo o formulário de login e dando a opção de criar uma nova conta
extends layout block content div.container div.row div.col-sm-6.col-md-4.col-md-offset-4 h1.text-center.login-title Sign in to our Passport app div.account-wall img(class="profile-img", src="https://lh5.googleusercontent.com/-b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120") form(class="form-signin", action='/login', method='POST') input(type="text", name="username" class="form-control", placeholder="Email",required, autofocus) input(type="password", name="password" class="form-control", placeholder="Password", required) button(class="btn btn-lg btn-primary btn-block", type="submit") Sign in span.clearfix a(href="http://code.tutsplus.com/signup", class="text-center new-account") Create an account #message if message h1.text-center.error-message #{message}
Graças ao Bootstrap, nossa página de login agora se parece com
Precisamos de mais duas visualizações para detalhes de registro e para a página inicial do aplicativo:
-
register.jade
contém o formulário de inscrição -
home.jade
diz olá e mostra os detalhes do usuário conectado
Se você não estiver familiarizado com o Jade, confira a documentação.
Implementando a funcionalidade de logout
O Passport, sendo um middleware, tem permissão para adicionar certas propriedades e métodos em objetos de solicitação e resposta e faz uso adequado dele adicionando um muito útil request.logout()
método que invalida a sessão do usuário além de outras propriedades.
/* Handle Logout */ router.get('/signout', function(req, res) { req.logout(); res.redirect('/'); });
Protegendo Rotas
O Passport também oferece a capacidade de proteger o acesso a uma rota considerada imprópria para um usuário anônimo. Isso significa que se algum usuário tentar acessar http://localhost:3000/home sem se autenticar na aplicação, ele será redirecionado para a página inicial fazendo
/* GET Home Page */ router.get('/home', isAuthenticated, function(req, res){ res.render('home', { user: req.user }); }); // As with any middleware it is quintessential to call next() // if the user is authenticated var isAuthenticated = function (req, res, next) { if (req.isAuthenticated()) return next(); res.redirect('/'); }
Conclusão
O Passport não é o único jogador nessa área quando se trata de autenticar aplicativos Node.js e existem alternativas como EveryAuth, mas a modularidade, flexibilidade, suporte da comunidade e o fato de ser apenas um middleware tornam o Passport definitivamente uma escolha muito melhor.
Para uma comparação detalhada entre os dois, aqui está uma perspectiva interessante e informativa do próprio desenvolvedor do Passport.
Se você quiser ver o que mais você pode fazer com o Node.js, confira a variedade de itens do Node.js no Envato Market, desde um formulário de contato AJAX responsivo até um encurtador de URL ou até mesmo um gerador de CRUD de banco de dados.
Originally posted 2022-06-22 21:58:57.