Як автентифікувати та авторизувати користувача за допомогою JWT у NodeJS

Автентифікація та авторизація є основною концепцією комп’ютерної безпеки. Ви використовуєте свої облікові дані (наприклад, ім’я користувача та пароль), щоб підтвердити свою особу та ідентифікувати себе як зареєстрованого користувача, а потім отримати додаткові привілеї.

Це також стосується випадків входу в онлайн-сервіси за допомогою облікових записів Facebook або Google.

У цій статті ми збираємося створити Nodejs API з автентифікацією JWT (JSON Web Tokens). Інструменти, які ми збираємося використовувати в цьому посібнику:

  • Expressjs
  • База даних MongoDB
  • Мангуст
  • Дотенв
  • Bcryptjs
  • Jsonwebtoken

Автентифікація Vs. Авторизація

Що таке автентифікація?

Автентифікація – це процес ідентифікації користувачів шляхом отримання облікових даних, таких як електронна пошта, пароль і маркери. Надані облікові дані порівнюються з обліковими даними зареєстрованого користувача, доступними у файлі локальної комп’ютерної системи або будь-яких базах даних. Якщо надані облікові дані збігаються з наявними даними в базі даних, процес автентифікації завершено, і користувач отримує доступ до ресурсів.

Що таке авторизація?

Авторизація відбувається після аутентифікації. Кожна авторизація повинна мати процес автентифікації. Це процес надання користувачам доступу до ресурсів систем або веб-сайту. У цьому підручнику ми будемо дозволяти користувачам, які ввійшли в систему, отримати доступ до даних користувача. Якщо користувач не ввійшов у систему, він не зможе отримати доступ до даних.

Найкращими прикладами авторизації є соціальні медіа-платформи, такі як Facebook і Twitter. Ви не можете отримати доступ до вмісту соціальних мереж, не маючи облікового запису.

Іншим прикладом авторизації є вміст на основі підписки. Ваша автентифікація може бути здійснена шляхом входу на веб-сайт, але ви не матимете доступу до вмісту, доки ви не підписалися.

Попередня умова

Перш ніж рухатися вперед, я припускаю, що ви маєте базові знання про Javascript і MongoDB і добре знаєте Nodejs.

Переконайтеся, що ви встановили node і npm на вашій локальній машині. Щоб перевірити, чи на вашому комп’ютері встановлено node і npm, відкрийте командний рядок і введіть node -v і npm -v. Це повинно показати наступний результат.

Ваші версії можуть відрізнятися від моїх. NPM автоматично завантажується разом із вузлом. Якщо ви ще не завантажили його, завантажте його з Веб-сайт NodeJS.

Для написання коду вам знадобиться IDE (інтегроване середовище розробки). У цьому посібнику я використовую редактор коду VS. Якщо у вас є інший, ви також можете використовувати його. Якщо на вашому комп’ютері не встановлено IDE, ви можете завантажити його з Веб-сайт Visual Studio. Завантажте його на основі вашої локальної системи.

Налаштування проекту

Створіть папку з назвою nodeapi будь-де на локальному комп’ютері, а потім відкрийте її за допомогою vs-code. Відкрийте термінал vs-code, а потім ініціалізуйте менеджер пакетів вузлів, ввівши.

npm init -y

Переконайтеся, що ви перебуваєте в каталозі nodeapi.

Наведена вище команда створить файл package.json, який містить усі залежності, які ми збираємося використовувати в цьому проекті.

  Виправте неправильний параметр

Тепер ми завантажимо всі пакети, згадані вище, тепер введіть і введіть їх у терміналі.

npm install express dotenv jsonwebtoken mongoose bcryptjs

Тепер у вас будуть файли та папки, як показано нижче.

Створення сервера та підключення бази даних

Тепер створіть файл з назвою index.js і папку з назвою config. У конфігурації створіть два файли з назвою conn.js для підключення до бази даних і config.env для оголошення змінних середовища. Запишіть наведений нижче код у відповідні файли.

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 

//Creating an app from express
const app = express();

//Using express.json to get request of json data
app.use(express.json());



//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Якщо ви використовуєте dotenv, налаштуйте його у файлі index.js перед викликом інших файлів, які використовують змінні середовища.

conn.js

const mongoose = require('mongoose');

mongoose.connect(process.env.URI, 
    { useNewUrlParser: true,
     useUnifiedTopology: true })
    .then((data) => {
        console.log(`Database connected to ${data.connection.host}`)
})

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000

Я використовую mongo-DB Atlas URI, ви також можете використовувати localhost.

Створення моделей і маршрутів

Модель — це макет ваших даних у базі даних Mongo-DB, який зберігатиметься як документ JSON. Для створення моделі ми будемо використовувати схему mongoose.

Маршрутизація стосується того, як програма відповідає на запити клієнта. Ми будемо використовувати функцію Express Router для створення маршрутів.

Методи маршрутизації зазвичай приймають два аргументи. Перший — маршрут, а другий — функція зворотного виклику, щоб визначити, що цей маршрут робитиме за запитом клієнта.

Він також приймає третій аргумент як функцію проміжного програмного забезпечення, коли це необхідно, наприклад, у процесі автентифікації. Оскільки ми створюємо автентифікований API, ми також будемо використовувати функцію проміжного програмного забезпечення для авторизації та автентифікації користувачів.

Тепер ми створимо дві папки з назвами routes і models. Усередині маршрутів створіть ім’я файлу userRoute.js, а в папці models створіть ім’я файлу userModel.js. Після створення файлів запишіть наступний код у відповідні файли.

userModel.js

const mongoose = require('mongoose');

//Creating Schema using mongoose
const userSchema = new mongoose.Schema({
    name: {
        type:String,
        required:true,
        minLength:[4,'Name should be minimum of 4 characters']
    },
    email:{
        type:String,
        required:true,
        unique:true,
    },
    password:{
        type:String,
        required:true,
        minLength:[8,'Password should be minimum of 8 characters']
    },
    token:{
        type:String
    }
})

//Creating models
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;

userRoute.js

const express = require('express');
//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post('/register',(req,res)=>{

})
//Creating login routes
route.post('/login',(req,res)=>{

})

//Creating user routes to fetch users data
route.get('/user',(req,res)=>{

})

Реалізація функцій маршрутизації та створення токенів JWT

Що таке JWT?

Веб-токени JSON (JWT) — це бібліотека JavaScript, яка створює та перевіряє маркери. Це відкритий стандарт, який використовується для обміну інформацією між двома сторонами – клієнтом і сервером. Ми будемо використовувати дві функції JWT. Перша функція sign для створення нового маркера, а друга функція verify для перевірки маркера.

Що таке bcryptjs?

Bcryptjs — це функція хешування, створена Нільсом Провосом і Девідом Мазьєром. Він використовує хеш-алгоритм для хешування пароля. Він має дві найпоширеніші функції, які ми будемо використовувати в цьому проекті. Перша функція bcryptjs — це хеш для генерування хеш-значення, а друга функція — функція порівняння для порівняння паролів.

  Як видалити свої записи Alexa за допомогою голосу

Реалізація функцій маршруту

Функція зворотного виклику в маршрутизації приймає три аргументи: запит, відповідь і наступну функцію. Наступний аргумент необов’язковий; передавайте це лише тоді, коли вам це потрібно. Ці аргументи мають бути в запиті, відповіді та наступному наказі. Тепер змініть файли userRoute.js, config.env і index.js за допомогою наступних кодів.

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post("/register", async (req, res) => {

    try {
        const { name, email, password } = req.body;
        //Check emptyness of the incoming data
        if (!name || !email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }

        //Check if the user already exist or not
        const userExist = await userModel.findOne({ email: req.body.email });
        if (userExist) {
            return res.json({ message: 'User already exist with the given emailId' })
        }
        //Hash the password
        const salt = await bcrypt.genSalt(10);
        const hashPassword = await bcrypt.hash(req.body.password, salt);
        req.body.password = hashPassword;
        const user = new userModel(req.body);
        await user.save();
        const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({ 'token': token }).json({ success: true, message: 'User registered successfully', data: user })
    } catch (error) {
        return res.json({ error: error });
    }

})
//Creating login routes
route.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        //Check emptyness of the incoming data
        if (!email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }
        //Check if the user already exist or not
        const userExist = await userModel.findOne({email:req.body.email});
        if(!userExist){
            return res.json({message:'Wrong credentials'})
        }
        //Check password match
        const isPasswordMatched = await bcrypt.compare(password,userExist.password);
        if(!isPasswordMatched){
            return res.json({message:'Wrong credentials pass'});
        }
        const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({"token":token}).json({success:true,message:'LoggedIn Successfully'})
    } catch (error) {
        return res.json({ error: error });
    }

})

//Creating user routes to fetch users data
route.get('/user', async (req, res) => {
    try {
        const user  = await userModel.find();
        if(!user){
            return res.json({message:'No user found'})
        }
        return res.json({user:user})
    } catch (error) {
        return res.json({ error: error });  
    }
})

module.exports = route;

Якщо ви використовуєте функцію Async, використовуйте блок try-catch, інакше виникне необроблена помилка відхилення обіцянки.

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000
SECRET_KEY = KGGK>HKHVHJVKBKJKJBKBKHKBMKHB
JWT_EXPIRE = 2d

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Using routes

app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Створення проміжного ПЗ для автентифікації користувача

Що таке проміжне ПЗ?

Проміжне програмне забезпечення — це функція, яка має доступ до запиту, об’єкта відповіді та наступної функції в циклі запит-відповідь. Наступна функція викликається після завершення виконання функції. Як я вже згадував вище, використовуйте next(), коли вам потрібно виконати іншу функцію зворотного виклику або функцію проміжного програмного забезпечення.

Тепер створіть папку під назвою проміжне програмне забезпечення, а всередині неї створіть ім’я файлу як auth.js і напишіть наступний код.

  Скільки мені потрібно кожного місяця?

auth.js

const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
    try {
        const {token} = req.cookies;
        if(!token){
            return next('Please login to access the data');
        }
        const verify = await jwt.verify(token,process.env.SECRET_KEY);
        req.user = await userModel.findById(verify.id);
        next();
    } catch (error) {
       return next(error); 
    }
}

module.exports = isAuthenticated;

Тепер установіть бібліотеку аналізатора файлів cookie, щоб налаштувати аналізатор файлів cookie у своїй програмі. cookieParser допомагає отримати доступ до маркера, що зберігається в файлі cookie. Якщо у вашому додатку nodejs не налаштовано cookieParser, ви не зможете отримати доступ до файлів cookie із заголовків об’єкта запиту. Тепер напишіть у терміналі, щоб завантажити парсер cookie.

npm i cookie-parser

Тепер у вас встановлено cookieParser. Налаштуйте свою програму, змінивши файл index.js і додавши проміжне програмне забезпечення до маршруту «/user/».

файл index.js

const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Configuring cookie-parser
app.use(cookieParser()); 

//Using routes
app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating user routes to fetch users data
route.get('/user', isAuthenticated, async (req, res) => {
    try {
        const user = await userModel.find();
        if (!user) {
            return res.json({ message: 'No user found' })
        }
        return res.json({ user: user })
    } catch (error) {
        return res.json({ error: error });
    }
})

module.exports = route;

Маршрут “/user” доступний лише тоді, коли користувач увійшов у систему.

Перевірка API на POSTMAN

Перш ніж перевіряти API, потрібно змінити файл package.json. Додайте наступні рядки коду.

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },

Ви можете запустити сервер, ввівши npm start, але він запуститься лише один раз. Щоб підтримувати роботу вашого сервера під час зміни файлів, вам знадобиться nodemon. Завантажте його, ввівши в терміналі

npm install -g nodemon

Прапорець -g завантажить nodemon глобально у вашу локальну систему. Вам не потрібно завантажувати його знову і знову для кожного нового проекту.

Щоб запустити сервер, введіть npm run dev у терміналі. Ви отримаєте наступний результат.

Нарешті ваш код завершено, і сервер працює правильно, перейдіть до листоноші та перевірте, чи він працює.

Що таке POSTMAN?

POSTMAN — це програмний інструмент для проектування, створення, розробки та тестування API.

Якщо ви не завантажили листоношу на свій комп’ютер, завантажте її з сайт листоноші.

Тепер відкрийте листоношу та створіть назву колекції nodeAPItest, а всередині неї створіть три запити: register, login і user. Ви повинні мати такі файли.

Коли ви надсилаєте дані JSON на “localhost:5000/api/register”, ви отримаєте такий результат.

Оскільки під час реєстрації ми також створюємо та зберігаємо токени у файли cookie, ви можете отримати інформацію про користувача, коли запитуєте маршрут «localhost:5000/api/user». Решту запитів ви можете перевірити на POSTMAN.

Якщо вам потрібен повний код, ви можете отримати його у мене обліковий запис github.

Висновок

У цьому підручнику ми навчилися застосовувати автентифікацію до API NodeJS за допомогою токенів JWT. Ми також надали користувачам доступ до даних користувачів.

ЩАСЛИВОГО КОДУВАННЯ!