Рубрики
Без рубрики

Методы аутентификации Nodejs (часть 1)

Методы реализации аутентификации в Nodejs

Автор оригинала: Abdul-Samii Ajala.

Аутентификация предназначена для идентификации пользователей и предоставление прав доступа и содержания в зависимости от их ID.It является неотъемлемой частью веб-разработки, которую мы не можем позволить себе подорвать свою безопасность.

В этой статье мы смотрим на разные методы/способы, с помощью которых мы можем реализовать аутентификацию в наших приложениях Nodejs. Он будет основан на API, и мы создадим общественность, а также секретные конечные точки. Затем мы будем использовать каждую из наших методов аутентификации для реализации безопасности этих конечных точек. Это предназначено для объяснения методов аутентификации. Для его реализации на производстве необходимо учитывать дополнительные меры безопасности, такие как проверка данных.

Эта статья будет в двух (2) частях, и мы будем рассматривать следующие методы аутентификации

  • Аутентификация на основе сессии
  • Аутентификация на основе токена
  • Аутентификация по паролю

В этом руководстве мы рассмотрим пять (5) ключевые аспекты аутентификации в каждом метках. Это процесс регистрации, вход, авторизация, выход и пароль сброс

Прежде чем мы посмотрим в них один за другим, давайте настроим нашу модель API.

Мы будем использовать следующие пакеты узлов в зависимости от метода аутентификации, который мы выбираем использовать.

  • Экспресс: минималистичные рамки для строительства веб-приложений
  • Экспресс-сессия: для управления сеансом в вашей экспресс-приложении
  • BCRYPT: шифрование и дешифрование паролей
  • BodyParser: для анализа данных в теле запросов, отправленных на сервер
  • NodEmailer: для отправки электронной почты (пароль сброс электронной почты в этом случае)
  • NodeMailer-SMTP-Transport: для настройки настройки транспорта SMTP для NODEMAIVER
  • Mongoose: DRM (управление соотношением данных) для управления базой данных MongoDB
  • JSanWebTokko: для реализации аутентификации на основе токена
  • Reforid: для генерации уникальных ключей при реализации сброса пароля

Наш сервер/API

Требования

  1. Nodejs должны быть установлены. Посетить Nodejs.org Чтобы установить на свою машину
  2. MongoDB (я рекомендую MLAB )
  3. Любая среда командной строки (I.E Terminal, ITERM, CMD, PowerShell, Git Bash E.T.c)
  4. Текстовый редактор. Я лично рекомендую Визуальный студийный код

Я буду использовать ES6 в этом руководстве, поскольку он используется более популярно в сообществе JavaScript, а также выдерживать испытание временем

Наша структура файлов проекта

nodeAuthTut
  |-- models
    |    |-- user
    |-- app.js
    |-- package.json

Так что давайте начнем,

Давайте сделаем некоторые упражнения командной строки. Давайте создадим нашу папку и файлы.

  • Откройте желаемый терминал
  • Перейдите к своему настольному столу (или на желаемое рабочее пространство) CD Desktop
  • Создайте нашу папку Project, запустите mkdir nodeauthtut && cd nodeauthtut
  • Создайте папку наших моделей в нашей папке проекта, запустите MKDIR Models && CD Models && Touch User.js
  • Измените каталог обратно в нашу корневую папку CD ..
  • Запустите это в корне Nodeauthtut Каталог:
npm init -y
  • Затем мы устанавливаем наши зависимости, как так
npm i -S express body-parser express-session bcrypt nodemailer nodemailer-smtp-transport jsonwebtoken mongoose shortid
  • Чтобы создать нашу точку входа приложения, запустите Нажмите App.js , Вот наш начальный код App.js ниже:
// define dependencies
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const app = express();
const PORT = 3000; // you can change this if this port number is not available

//connect to database
mongoose.connect('mongodb://localhost:27017/auth_tuts', {
  useMongoClient: true
  } (err, db) => {
    if (err) {
      console.log("Couldn't connect to database");
    } else {
      console.log(`Connected To Database`);
    }
  }
);

// define database schemas
const user = require('./model/user'); // we shall create this (model/user.js) soon 

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

app.get('/', (req, res) => {
  res.send('Welcome to the Home of our APP');
})

app.get('/protected', (req, res) => {
  res.send('This page is protected. It requires authentication');
})

app.listen(PORT, () => {
  console.log(`app running port ${PORT}`)
})

В нашей модели/user.js у нас есть это

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// we create a user schema
let userSchema = new Schema({
  fullname: {
    type: String,
    required: true,
    trim: true
  },
  email: {
    type: String,
    required: true,
    trim: true,
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true
  },
  gender: {
    type: String,
    required: true
  },
  passResetKey: String,
  passKeyExpires: Number,
  createdAt: {
    type: Date,
    required: false
  },
  updatedAt: {
    type: Number,
    required: false
  },
}, {runSettersOnQuery: true}); // 'runSettersOnQuery' is used to implement the specifications in our model schema such as the 'trim' option.

userSchema.pre('save', function (next) {
  this.email = this
    .email
    .toLowerCase(); // ensure email are in lowercase

  var currentDate = new Date().getTime();
  this.updatedAt = currentDate;
  if (!this.created_at) {
    this.createdAt = currentDate;
  }
  next();
})

var user = mongoose.model('user', userSchema);

module.exports = user;

Откройте свою папку в любом предпочтительном текстовом редакторе, скопируйте код выше и вставьте его внутрь. Прежде, если мы осмотрим методы аутентификации один за другим, обновите раздел сценария вашего Package.json вроде так

{
  ...
    "script": {
    	"start": "node app.js"
    }
}

Затем запустите NPM NPM начать Попробуйте http://localhost: 3000 и http://localhost: 3000/защищенный (или ваш настроенный номер порта) в вашем браузере или почтальоном. Вы обнаружите, что оба дают нам благоприятный ответ, хотя мы не аутентифицируемся доступа к защищенному маршруту.

Фото зачисления: (Бехнам Собххиз) [https://www.uplabs.com/behnamsobhhiz]

Экран входа в систему

I. Аутентификация на основе сессии

В аутентификации на основе сессии, учетные данные пользователя (имя пользователя/адрес электронной почты и пароль, например) сравниваются с тем, что хранится в базе данных, и если они совпадают, сеанс инициализируется для пользователя с выбором. Эти сеансы прекращаются на выход пользователя, и они предназначены для истечения после сконфигурированного времени.

Для реализации этого обновления вашего App.js нравится так

// define dependencies
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const shortid = require('shortid');
const session = require('express-session'); //we're using 'express-session' as 'session' here
const bcrypt = require("bcrypt"); // 
const app = express();
const PORT = 3000; // you can change this if this port number is not available

//connect to database
mongoose.connect('mongodb://localhost:27017/auth_tuts', { //replace this with you
  useMongoClient: true
  } (err, db) => {
    if (err) {
      console.log("Couldn't connect to database");
    } else {
      console.log(`Connected To Database`);
    }
  }
);

// define database schemas
const User = require('./model/user'); // we shall create this (model/user.js) soon 

// configure bodyParser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(
  session({
    secret: "iy98hcbh489n38984y4h498", // don't put this into your code at production.  Try using saving it into environment variable or a config file.
    resave: true,
    saveUninitialized: false
  })
);

/*
0. Unprotected route
=============
*/
app.get('/', (req, res) => {
  res.send('Welcome to the Home of our APP');
})

/*
1. User Sign up
=============
*/
// here we're expecting username, fullname, email and password in body of the request for signup. Note that we're using post http method
app.post('/signup', (req, res) => {
  let {username, fullname, email, password} = req.body; // this is called destructuring. We're extracting these variables and their values from 'req.body'
    
    let userData = {
    	username,
        password: bcrypt.hashSync(password, 5), // we are using bcrypt to hash our password before saving it to the database
        fullname,
        email
    };
    
    let newUser = new User(userData);
    newUser.save().then(error => {
    	if (!error) {
        	return res.status(201).json('signup successful')
        } else {
        	if (error.code ===  11000) { // this error gets thrown only if similar user record already exist.
            	return res.status(409).send('user already exist!')
            } else {
            	console.log(JSON.stringigy(error, null, 2)); // you might want to do this to examine and trace where the problem is emanating from
            	return res.status(500).send('error signing up user')
            }
        }
    })
})

/*
2. User Sign in
=============
*/
We will be using username and password, but it can be improved or modified (e.g email and password or some other ways as you please)
app.post('/login', (req, res) => {
  let {username, password} = req.body;
    User.findOne({username: username}, 'username email password', (err, userData) => {
    	if (!err) {
        	let passwordCheck = bcrypt.compareSync(password, userData.password);
        	if (passwordCheck) { // we are using bcrypt to check the password hash from db against the supplied password by user
                req.session.user = {
                  email: userData.email,
                  username: userData.username
                  id: userData._id
                }; // saving some user's data into user's session
                req.session.user.expires = new Date(
                  Date.now() + 3 * 24 * 3600 * 1000; // session expires in 3 days
                );
                res.status(200).send('You are logged in, Welcome!');
            } else {
            	res.status(401).send('incorrect password');
            }
        } else {
        	res.status(401).send('invalid login credentials')
        }
    })
})

/*
3. authorization
=============
A simple way of implementing authorization is creating a simple middleware for it. Any endpoint that come after the authorization middleware won't pass if user doesn't have a valid session
*/
app.use((req, res, next) => {
  if (req.session.user) {
    next();
  } else {
    res.status(401).send('Authrization failed! Please login');
  }
});

app.get('/protected', (req, res) => {
  res.send(`You are seeing this because you have a valid session.
    	Your username is ${req.session.user.username} 
        and email is ${req.session.user.email}.
    `)
})

/*
4. Logout
=============
*/
app.all('/logout', (req, res) => {
  delete req.session.user; // any of these works
  	req.session.destroy(); // any of these works
    res.status(200).send('logout successful')
})

/*
4. Password reset
=================
We shall be using two endpoints to implement password reset functionality
*/
app.post('/forgot', (req, res) => {
  let {email} = req.body; // same as let email = req.body.email
  User.findOne({email: email}, (err, userData) => {
    if (!err) {
      userData.passResetKey = shortid.generate();
      userData.passKeyExpires = new Date().getTime() + 20 * 60 * 1000 // pass reset key only valid for 20 minutes
      userData.save().then(err => {
          if (!err) {
            // configuring smtp transport machanism for password reset email
            let transporter = nodemailer.createTransport({
              service: "gmail",
              port: 465,
              auth: {
                user: '', // your gmail address
                pass: '' // your gmail password
              }
            });
            let mailOptions = {
              subject: `NodeAuthTuts | Password reset`,
              to: email,
              from: `NodeAuthTuts `,
              html: `
                

Hi,

Here is your password reset key

${passResetKey}

Please ignore if you didn't try to reset your password on our platform

`; }; try { transporter.sendMail(mailOptions, (error, response) => { if (error) { console.log("error:\n", error, "\n"); res.status(500).send("could not send reset code"); } else { console.log("email sent:\n", response); res.status(200).send("Reset Code sent"); } }); } catch (error) { console.log(error); res.status(500).send("could not sent reset code"); } } }) } else { res.status(400).send('email is incorrect'); } }) }); app.post('/resetpass', (req, res) => { let {resetKey, newPassword} = req.body User.find({passResetKey: resetKey}, (err, userData) => { if (!err) { let now = new Date().getTime(); let keyExpiration = userDate.passKeyExpires; if (keyExpiration > now) { userData.password = bcrypt.hashSync(newPassword, 5); userData.passResetKey = null; // remove passResetKey from user's records userData.keyExpiration = null; userData.save().then(err => { // save the new changes if (!err) { res.status(200).send('Password reset successful') } else { res.status(500).send('error resetting your password') } }) } else { res.status(400).send('Sorry, pass key has expired. Please initiate the request for a new one'); } } else { res.status(400).send('invalid pass key!'); } }) }) app.listen(PORT, () => { console.log(`app running port ${PORT}`) })

Мы видели, как реализовать простую аутентификацию на основе сеанса, и теперь мы можем реализовать вход в систему, регистрацию, авторизацию, выйти и пароль/обновить/обновить.

Мы закончим двумя другими методами (токен и авторитетным аутером) в следующей части этой статьи. Мне понравится ваши комментарии, вопросы и версии в разделе комментариев ниже