Встречайте мой последний проект, я создал аутентификацию пользователей с помощью Passport.js и Bcrypt🎉


Необходимые условия : Javascript ES6

🍁Passport.js

Это промежуточное ПО аутентификации для приложений на базе Express, использующих Node.js. Будучи промежуточным ПО, он имеет доступ к объектам запроса и ответа и может манипулировать ими в цикле запрос-ответ. Библиотека passport.js состоит из двух отдельных библиотек. Первая — это основная библиотека Passport JS, а вторая — соответствующая библиотека стратегии.

  • Основная библиотека Passport JS требуется всегда и используется для поддержания информации о сеансе для аутентифицированных пользователей (т.е. вы будете импортировать эту библиотеку независимо от типа стратегии, которую вы будете использовать для аутентификации пользователя).
  • Библиотека вторичных стратегий зависит от методики, которую вы планируете использовать для аутентификации пользователя. Например, passport-local, passport-facebook, passport-oauth-google и т.д. Пользователи могут быть аутентифицированы по имени пользователя/паролю, сохраненному в базе данных, которую вы создали локально или в облаке (так называемая локальная стратегия), ИЛИ пользователи могут быть аутентифицированы путем входа в свой аккаунт Google (стратегия аутентификации Google), или Facebook, или Github.

Фреймворк Passport JS абстрагирует процесс входа в систему на две отдельные части: управление сессией (выполняется библиотекой Passport JS) и аутентификация (выполняется вторичной библиотекой Strategy, например, passport-local или passport-facebook или passport-oauth-google и т.д.).

Здесь вступает в действие express.js,

Библиотека Passport JS соединяется с библиотекой express-session и формирует базовые подмостки для присоединения (аутентифицированной) информации о пользователе к объекту req.session. Основная библиотека Passport JS работает с уже аутентифицированными пользователями и не играет никакой роли в фактической аутентификации пользователей. Ее единственная цель — поддерживать (присоединять) (уже аутентифицированного) пользователя к сессиям.

🍁Bcrypt

Очень важно иметь безопасную систему для сохранения пароля в интернет-бизнесе. Сохранять пароль непосредственно в базе данных не рекомендуется. Это может привести к нарушению безопасности. Чтобы сохранить пароль без нарушения безопасности и обеспечить пользователям конфиденциальность их личных данных, необходимо следовать некоторым протоколам при создании приложения.

Обычно разработчики запускают функцию хэширования, чтобы преобразовать пароль в нечто, что выглядит совершенно иначе, чем его первоначальная форма, с помощью математического алгоритма. Этот процесс называется хэшированием, а математический алгоритм — алгоритмом хэширования. Известный пакет node.js — «bcrypt», который скачивают в среднем 590 489 раз в неделю (о да, это много 💰).

Bcrypt также вводит технику соли. Для того чтобы повысить сложность защиты паролей и защитить их от атак, предпринимается дополнительный шаг, называемый солерованием пароля. Соление хэша в области криптографии означает добавление дополнительной строки из 32 или более символов к паролю перед его хэшированием.

Давайте продемонстрируем это на примере.

Майкл и Боб используют один и тот же пароль s@1t3dH@shBrown по совпадению, у них будет одинаковый хэш: $2a$12$xdWgQ5mhv8rSaUK3qdusTO4XdMFbQi6TD/1VvOZjvGm10RXnhZZa2.

Однако, если пароль Майклса соленый Iwx2ZE, а пароль Боба соленый 0DoVej, у них будут совершенно разные соленые хэши.

Майкл

Пароль: s@1t3dH@shBrown.

Соль: Iwx2ZE.

Соленый вход: Iwx2ZEs@1t3dH@shBrown

Выход соленого хэша: $2a$12$TGRg8FCZvnDm.f4WPNtWQucwRv5zsi4D9Qy/gYgpfFfYx9XpXdE6a

Боб

Пароль: s@1t3dH@shBrown

Соль: 0DoVej

Соленый вход: 0DoVejs@1t3dH@shBrown

Выход соленого хэша: $2a$12$VtpXTHf69x1db/71bGHl3eMiEDAkgQe/Gq6UeNOKuHvdg.WnIXEHa

Как вы можете видеть, их соленые хэши совершенно разные, несмотря на то, что они используют один и тот же пароль.

🍁Построим аутентификацию пользователя

🍀Установите необходимый пакет и запустите сервер

Введите npm init и установите следующие пакеты,

  • express
  • express-session
  • паспорт
  • passport-local
  • bcryptjs

Теперь создайте файл, app.js, и добавьте следующий код для создания сервера.

const express = require("express");
const app = express();
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});

Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете использовать dotenv для хранения номера PORT.

🍀Установка базы данных

Мы будем использовать mongoDB, поэтому давайте установим ее

npm i mongoose

создадим файл db.js и настроим базу данных

const mongoose = require('mongoose')

function Dbconnect(){

    const url = mongodb://localhost:27017/user;
    mongoose.connect(url,{ useNewUrlParser: true, useUnifiedTopology: true
        }).then(()=>{
            console.log('Connection Successful');
        }).catch((error)=>{     
            console.log('Something went wrong', error)
        });
}

module.exports = Dbconnect

Войдите в полноэкранный режим Выйдите из полноэкранного режима

Теперь создайте файл UserModel.js и добавьте в него следующее.

const mongoose = require('mongoose')
const {Schema} = mongoose
const UserSchema = new Schema ({
 email: {
 type: String,
 required: true
 },
 password: {
 type: String,
 required: true
 }
})
const UserModel = mongoose.model('user', UserSchema);
module.exports = UserModel;

Вход в полноэкранный режим Выйти из полноэкранного режима

Прежде чем хранить пароль, его необходимо зашифровать в целях безопасности. Вы будете использовать bcrypt , очень полезный пакет npm, который упрощает работу с зашифрованными паролями.

npm i bcrypt

Измените UserModel.js, чтобы зашифровать пароль перед сохранением в базе данных.

const mongoose = require('mongoose')
const bcrypt = require('bcryptjs');
const {Schema} = mongoose

const UserSchema = new Schema ({
...
})
UserSchema.pre('save', async function(next) {
 try {
 // check method of registration
 const user = this;
 if (!user.isModified('password')) next();
 // generate salt
 const salt = await bcrypt.genSalt(10);
 // hash the password
 const hashedPassword = await bcrypt.hash(this.password, salt);
 // replace plain text password with hashed password
 this.password = hashedPassword;
 next();
 } catch (error) {
 return next(error);
 }
 });
...
const User = mongoose.model('User', UserSchema);

Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы используем хук предварительного сохранения для изменения пароля перед его сохранением. Идея заключается в том, чтобы хранить хэш-версию пароля вместо обычного текста пароля. Хэш — это длинная сложная строка, сгенерированная из обычного текста.

Используйте функцию isModified, чтобы проверить, изменяется ли пароль, поскольку вам нужно хэшировать только новые пароли. Затем сгенерируйте соль и передайте ее вместе с обычным текстовым паролем в метод hash для генерации хэшированного пароля. Наконец, замените обычный пароль хэшированным паролем в базе данных.

В app.js подключитесь к базе данных.

// connect to db
const db = require('./db');
db.connect();

Войдите в полноэкранный режим Выход из полноэкранного режима

🍀Установка паспорта

Установите Passport и passport-local. Вы будете использовать эти пакеты для регистрации и входа пользователей.

npm i passport
npm i passport-local

Войти в полноэкранный режим Выйдите из полноэкранного режима

Создайте новый файл passportConfig.js и импортируйте passport-local и UserModel.js.

const LocalStraregy = require("passport-local").Strategy;
const User = require("./userModel");

Вход в полноэкранный режим Выход из полноэкранного режима

Настройте Passport для обработки регистрации пользователей.

const LocalStrategy = require("passport-local");
const User = require("./userModel");
module.exports = (passport) => {
 passport.use(
 "local-signup",
 new LocalStrategy(
 {
 usernameField: "email",
 passwordField: "password",
 },
 async (email, password, done) => {
 try {
 // check if user exists
 const userExists = await User.findOne({ "email": email });
 if (userExists) {
 return done(null, false)
 }
 // Create a new user with the user data provided
 const user = await User.create({ email, password });
 return done(null, user);
 } catch (error) {
 done(error);
 }
 }
 )
 );
}

Вход в полноэкранный режим Выход из полноэкранного режима

В приведенном выше коде вы проверяете, не используется ли уже данный email. Если email не существует, зарегистрируйте пользователя. Обратите внимание, что вы также настраиваете поле имени пользователя на прием электронной почты. По умолчанию passport-local ожидает имя пользователя, поэтому вам нужно указать ему, что вместо него вы передаете email.

Используйте passport-local для обработки входа пользователя в систему.

module.exports = (passport) => {
 passport.use(
 "local-signup",
 new localStrategy(
 ...
 )
 );
passport.use(
 "local-login",
 new LocalStrategy(
 {
 usernameField: "email",
 passwordField: "password",
 },
 async (email, password, done) => {
 try {
 const user = await User.findOne({ email: email });
 if (!user) return done(null, false);
 const isMatch = await user.matchPassword(password);
 if (!isMatch)
 return done(null, false);
 // if passwords match return user
 return done(null, user);
 } catch (error) {
 console.log(error)
 return done(error, false);
 }
 }
 )
 );
};

Вход в полноэкранный режим Выход из полноэкранного режима

Здесь проверяется, существует ли пользователь в базе данных, и если да, то проверяется, совпадает ли предоставленный пароль с паролем в базе данных. Обратите внимание, что вы также вызываете метод matchPassword() в модели пользователя, поэтому перейдите в файл UserModel.js и добавьте его.

UserSchema.methods.matchPassword = async function (password) {
 try {
 return await bcrypt.compare(password, this.password);
 } catch (error) {
 throw new Error(error);
 }
};

Вход в полноэкранный режим Выйти из полноэкранного режима

Этот метод сравнивает пароль от пользователя и пароль в базе данных и возвращает true, если они совпадают.

🍀Настройка маршрутов аутентификации

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

В app.js для регистрации пользователя используйте только что созданное промежуточное ПО аутентификации паспорта.

app.post("/auth/signup",
 passport.authenticate('local-signup', { session: false }),
 (req, res, next) => {
 // sign up
 res.json({
 user: req.user,
 });
 }
);

Вход в полноэкранный режим Выход из полноэкранного режима

В случае успеха маршрут регистрации должен вернуть созданного пользователя.

Далее создайте маршрут входа в систему.

app.post(
 "/auth/login",
 passport.authenticate('local-login', { session: false }),
 (req, res, next) => {
 // login
 res.json({
 user: req.user,
 });
 }
);

Войти в полноэкранный режим Выход из полноэкранного режима

🍀Добавление защищенных маршрутов

До сих пор вы использовали Passport для создания промежуточного ПО, которое регистрирует пользователя в базе данных, и другого, которое позволяет зарегистрированному пользователю войти в систему. Далее вы создадите промежуточное ПО авторизации для защиты конфиденциальных маршрутов с помощью JSON web-токена (JWT). Чтобы реализовать авторизацию JWT, вам необходимо:

Сгенерировать токен JWT. Передать токен пользователю. Пользователь отправит его обратно в запросах авторизации. Проверить токен, отправленный пользователем.

Для работы с JWT вы будете использовать пакет jsonwebtoken.

npm i jsonwebtoken

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

В файле app.js импортируйте jsonwebtoken и измените маршрут входа, как показано ниже.

app.post(
 "/auth/login",
 passport.authenticate('local-login', { session: false }),
 (req, res, next) => {
 // login
 jwt.sign({user: req.user}, 'secretKey', {expiresIn: '1h'}, (err, token) => {
 if(err) {
 return res.json({
 message: "Failed to login",
 token: null,
 });
 }
 res.json({
 token
 });
 })
 }
);

Вход в полноэкранный режим Выход из полноэкранного режима

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

Маршрут входа возвращает токен в случае успеха.

Используйте passport-jwt для доступа к защищенным маршрутам.

npm i passport-jwt В файле passportConfig.js настройте passport-jwt.

const JwtStrategy = require("passport-jwt").Strategy;
const { ExtractJwt } = require("passport-jwt")
module.exports = (passport) => {
passport.use(
 "local-login",
 new LocalStrategy(
 ...
 );
passport.use(
 new JwtStrategy(
 {
 jwtFromRequest: ExtractJwt.fromHeader("authorization"),
 secretOrKey: "secretKey",
 },
 async (jwtPayload, done) => {
 try {
 // Extract user
 const user = jwtPayload.user;
 done(null, user);
 } catch (error) {
 done(error, false);
 }
 }
 )
 );
};

Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что вы извлекаете JWT из заголовка авторизации, а не из тела запроса. Это не позволит хакерам перехватить запрос и получить токен.

Теперь вы готовы вывести аутентификацию пользователей на новый уровень.

Надеюсь, вам понравился этот небольшой учебник. Счастливого кодинга💗

Всегда помните, что никто не достиг вершины одним выстрелом. Им потребовалось гораздо больше борьбы и тяжелой работы, чем вы можете себе представить. Так что стремитесь к знаниям и продолжайте двигаться вперед. Спасибо

Оцените статью
devanswers.ru
Добавить комментарий