پیاده سازی احراز هویت در Node.js با Express و JWT — از صفر تا صد

۱۱۵۴ بازدید
آخرین به‌روزرسانی: ۰۳ مهر ۱۴۰۲
زمان مطالعه: ۷ دقیقه
پیاده سازی احراز هویت در Node.js با Express و JWT — از صفر تا صد

در این مقاله قصد داریم به بررسی روش پیاده‌سازی احراز هویت در Node.js با Express و JWT و MongoDB بپردازیم.

مقدمه

مراحل کار برای پیاده‌سازی یک سیستم احراز هویت در Node.js با استفاده از JWT، اکسپرس و همچنین پایگاه داده منگو به صورت زیر است:

پیش‌نیازها

شما باید دانش قبلی در مورد مبانی جاوا اسکریپت و Node.js داشته باشید. آشنا بودن با ساختار ES6 نیز یک مزیت محسوب می‌شود. همچنین دست کم باید Node.js روی سیستم نصب باشد.

پکیج‌های مورد نیاز

برای پیاده‌سازی پروژه مورد نظر به پکیج‌های npm زیر نیاز خواهید داشت.

  • Express: اکسپرس یک فریمورک وب اپلیکیشن کوچک و انعطاف‌پذیر Node.js است که مجموعه کاملی از قابلیت‌ها برای اپلیکیشن‌های وب و موبایل عرضه می‌کند.
  • Express-Validator: برای اعتبارسنجی داده‌ها روی سرور در فریمورک اکسپرس استفاده می‌شود. در واقع یک کتابخانه اعتبارسنجی داده‌ها در سمت سرور است. بنابراین حتی اگر یک کاربر اعتبارسنجی سمت کلاینت را نیز رد کند، در این جا گیر می‌افتد و خطایی صادر می‌شود.
  • Body-parser: یک میان‌افزار Node.js برای تجزیه داده‌های body است.
  • Bcryptjs: از این کتابخانه برای هش کردن رمز عبور استفاده می‌کنیم و سپس آن را در پایگاه داده ذخیره می‌نماییم. بدین ترتیب حتی مدیران اپلیکیشن نیز نمی‌توانند به حساب یک کاربر دسترسی داشته باشند.
  • JsonWebToken: از JsonWebToken بری رمزگذاری payload داده‌ها در زمان ثبت نام و بازگشت توکن استفاده می‌شود. می‌توانیم از آن توکن برای احراز هویت خودمان در صفحه‌های امن مانند داشبورد استفاده کنیم. همچنین گزینه‌ای برای تعیین میزان اعتبار این توکن‌ها وجود دارد و از این رو می‌توانید مدت زمانی که توکن دوام خواهد داشت را تعیین کنید.
  • Mongoose :Mongoose یک ابزار مدل‌سازی شیء MongoDB است که برای کار در یک محیط ناهمگام طراحی شده است. Mongoose هم از promises و هم callbacks پشتیبانی می‌کند.

پروژه اولیه

کار خود را با ایجاد یک پروژه Node آغاز می‌کنیم. بنابراین یک پوشه با نام node-auth ایجاد کرده و مراحل زیر را طی می‌کنیم. همه فایل‌های پروژه باید درون پوشه node-auth قرار داشته باشند:

npm init

دستور فوق برخی اطلاعات اولیه در مورد پروژه می‌پرسد. اینک که پروژه Node ایجاد شده است، نوبت آن رسیده است که پکیج‌های مورد نیاز را نصب کنیم. بنابراین با استفاده از دستور زیر اقدام به نصب پکیج‌های مورد نظر می‌کنیم:

npm install express express-validator body-parser bcryptjs jsonwebtoken mongoose –save

سپس یک فایل به نام index.js ایجاد کرده و کد زیر را در آن می‌نویسیم:

1// File : index.js
2const express = require("express");
3const bodyParser = require("body-parser");
4const app = express();
5// PORT
6const PORT = process.env.PORT || 4000;
7app.get("/", (req, res) => {
8  res.json({ message: "API Working" });
9});
10app.listen(PORT, (req, res) => {
11  console.log(`Server Started at PORT ${PORT}`);
12});

اکنون اگر دستور زیر را در ترمینال وارد کنید، سرور در پورت 4000 شروع به کار می‌کند.

node index.js

بدین ترتیب اپلیکیشن Node.js را با موفقیت راه‌اندازی کردیم. اکنون زمان آن رسیده است که پایگاه داده را راه‌اندازی کرده و کارکردهای بیشتری را به اپلیکیشن خود اضافه کنیم.

راه‌اندازی پایگاه داده MongoDB

برای ذخیره کاربران خود از پایگاه داده منگو استفاده می‌کنیم. بدین منظور می‌توانید از یک سرور ابری و یا از یک سرور لوکال منگو استفاده کنید. در این مقاله ما از یک سرور ابری منگو به نام mLab استفاده می‌کنیم. بنابراین ابتدا در وب‌سایت mLab (+) ثبت نام کنید و مراحل زیر را طی نمایید:

  • پس از ثبت نام روی دکمه Create New در صفحه اصلی وب‌سایت کلیک کنید.
  • سپس روی ارائه‌دهنده ابری کلیک کنید. مثلاً می‌توانید از AWS استفاده کنید. در زمان انتخاب نوع Plan گزینه free Sandbax را انتخاب کرده و سپس روی دکمه Continue در سمت راست-پایین کلیک کنید.
  • اینک منطقه را انتخاب کنید و روی Continue کلیک کنید.
  • یک نام پایگاه داده وارد کنید. ما از نام node-auth استفاده می‌کنیم. سپس روی Continue کلیک کنید و در صفحه بعدی سفارشی خود را ارسال کنید. جای نگرانی نیست چون این خدمات رایگان هستند.
  • اکنون به صفحه اصلی هدایت می‌شوید. پایگاه داده‌ای که ایجاد کردید را انتخاب کنید.
  • URI استاندارد منگو را کپی کنید.
  • اکنون باید یک کاربر به پایگاه داده خود اضافه کنید. از بین پنج زبانه تحتانی روی users کلیک کنید و سپس با کلیک روی Add Database User یک کاربر جدید اضافه کنید.
  • اکنون باید کاربر پایگاه داده را به دست آورده باشید. به جای && نام کاربر پایگاه داده و رمز عبور را وارد کنید:
mongodb://<dbuser>:<dbpassword>@ds257698.mlab.com:57698/node-auth
  • بنابراین آدرس سرور منگو باید مانند زیر باشد. توجه کنید که این آدرس مربوط به ما است و از یک نام کاربری و رمز عبور جعلی استفاده کرده‌ایم.
mongodb://test:hello1234@ds257698.mlab.com:57698/node-auth

اکنون که mongoURI را در اختیار دارید، آماده اتصال اپلیکیشن node-auth به پایگاه داده هستید. به این منظور باید مراحلی که در بخش بعدی توضیح داده شده است را طی کنید.

پیکربندی مدل کاربر

ابتدا یک پوشه به نام config ایجاد می‌کنیم. این پوشه اطلاعات اتصال به پایگاه داده را نگهداری می‌کند. فایلی به نام db.js در پوشه config ایجاد کنید:

1//FILENAME : db.js
2const mongoose = require("mongoose");
3// Replace this with your MONGOURI.
4const MONGOURI = "mongodb://testuser:testpassword@ds257698.mlab.com:57698/node-auth";
5const InitiateMongoServer = async () => {
6  try {
7    await mongoose.connect(MONGOURI, {
8      useNewUrlParser: true
9    });
10    console.log("Connected to DB !!");
11  } catch (e) {
12    console.log(e);
13    throw e;
14  }
15};
16module.exports = InitiateMongoServer;

اکنون که اتصال به پایگاه داده انجام یافته است، یک مدل کاربر برای ذخیره کاربران وارد شده به اپلیکیشن می‌سازیم. به این منظور پوشه جدیدی به نام model ایجاد کنید و درون آن یک فایل به نام User.js بسازید. در ادامه از Mongoose برای ایجاد UserSchema استفاده می‌کنیم.

فایل User.js

1//FILENAME : User.js
2const mongoose = require("mongoose");
3const UserSchema = mongoose.Schema({
4  username: {
5    type: String,
6    required: true
7  },
8  email: {
9    type: String,
10    required: true
11  },
12  password: {
13    type: String,
14    required: true
15  },
16  createdAt: {
17    type: Date,
18    default: Date.now()
19  }
20});
21// export model user with UserSchema
22module.exports = mongoose.model("user", UserSchema);

اکنون کار اتصال پایگاه داده و اسکیمای کاربر پایان یافته است. بنابراین می‌توانیم فایل index.js را به‌روزرسانی کنیم تا API-مان به پایگاه داده اتصال یابد.

فایل index.js

1const express = require("express");
2const bodyParser = require("body-parser");
3const InitiateMongoServer = require("./config/db");
4// Initiate Mongo Server
5InitiateMongoServer();
6const app = express();
7// PORT
8const PORT = process.env.PORT || 4000;
9// Middleware
10app.use(bodyParser.json());
11app.get("/", (req, res) => {
12  res.json({ message: "API Working" });
13});
14app.listen(PORT, (req, res) => {
15  console.log(`Server Started at PORT ${PORT}`);
16});

بدین ترتیب موفق شدیم اپلیکیشن را به سرور MongoDB وصل کنیم. اکنون کار بعدی این است که یک مسیر به صورت ‎/user/signup ایجاد کنیم تا کاربر جدید ثبت نام کند. این مسئله را در بخش بعدی پیگیری خواهیم کرد.

ثبت نام کاربر

مسیر ثبت نام کاربر به صورت user/signup/ است. یک پوشه به نام routes ایجاد کنید. در این پوشه یک فایل به نام user.js بسازید و محتوای آن را به صورت زیر تعیین کنید:

فایل routes/user.js

1// Filename : user.js
2const express = require("express");
3const { check, validationResult} = require("express-validator/check");
4const bcrypt = require("bcryptjs");
5const jwt = require("jsonwebtoken");
6const router = express.Router();
7const User = require("../model/User");
8/**
9 * @method - POST
10 * @param - /signup
11 * @description - User SignUp
12 */
13router.post(
14    "/signup",
15    [
16        check("username", "Please Enter a Valid Username")
17        .not()
18        .isEmpty(),
19        check("email", "Please enter a valid email").isEmail(),
20        check("password", "Please enter a valid password").isLength({
21            min: 6
22        })
23    ],
24    async (req, res) => {
25        const errors = validationResult(req);
26        if (!errors.isEmpty()) {
27            return res.status(400).json({
28                errors: errors.array()
29            });
30        }
31        const {
32            username,
33            email,
34            password
35        } = req.body;
36        try {
37            let user = await User.findOne({
38                email
39            });
40            if (user) {
41                return res.status(400).json({
42                    msg: "User Already Exists"
43                });
44            }
45            user = new User({
46                username,
47                email,
48                password
49            });
50            const salt = await bcrypt.genSalt(10);
51            user.password = await bcrypt.hash(password, salt);
52            await user.save();
53            const payload = {
54                user: {
55                    id: user.id
56                }
57            };
58            jwt.sign(
59                payload,
60                "randomString", {
61                    expiresIn: 10000
62                },
63                (err, token) => {
64                    if (err) throw err;
65                    res.status(200).json({
66                        token
67                    });
68                }
69            );
70        } catch (err) {
71            console.log(err.message);
72            res.status(500).send("Error in Saving");
73        }
74    }
75);

اکنون ثبت نام کاربر در فایل routes/user.js انجام می‌یابد. بنابراین باید آن را درون index.js ایمپورت کنیم تا کار کند. از این رو فایل index.js را طوری به‌روزرسانی می‌کنیم که به صورت زیر درآید:

1const express = require("express");
2const bodyParser = require("body-parser");
3const user = require("./routes/user"); //new addition
4const InitiateMongoServer = require("./config/db");
5// Initiate Mongo Server
6InitiateMongoServer();
7const app = express();
8// PORT
9const PORT = process.env.PORT || 4000;
10// Middleware
11app.use(bodyParser.json());
12app.get("/", (req, res) => {
13  res.json({ message: "API Working" });
14});
15/**
16 * Router Middleware
17 * Router - /user/*
18 * Method - *
19 */
20app.use("/user", user);
21app.listen(PORT, (req, res) => {
22  console.log(`Server Started at PORT ${PORT}`);
23});

ثبت نام کاربر را با یک postman آغاز می‌کنیم. postman ابزاری برای تست کردن API است:

احراز هویت در Node.js

ورود کاربر

اکنون زمان آن رسیده است که مسیریاب ورود را پیاده‌سازی کنیم که در مسیر ‎/user/login قرار دارد. قطعه کد برای کارکرد ورود به صورت زیر است. این کد را به user.js اضافه کنید:

1router.post(
2  "/login",
3  [
4    check("email", "Please enter a valid email").isEmail(),
5    check("password", "Please enter a valid password").isLength({
6      min: 6
7    })
8  ],
9  async (req, res) => {
10    const errors = validationResult(req);
11    if (!errors.isEmpty()) {
12      return res.status(400).json({
13        errors: errors.array()
14      });
15    }
16    const { email, password } = req.body;
17    try {
18      let user = await User.findOne({
19        email
20      });
21      if (!user)
22        return res.status(400).json({
23          message: "User Not Exist"
24        });
25      const isMatch = await bcrypt.compare(password, user.password);
26      if (!isMatch)
27        return res.status(400).json({
28          message: "Incorrect Password !"
29        });
30      const payload = {
31        user: {
32          id: user.id
33        }
34      };
35      jwt.sign(
36        payload,
37        "secret",
38        {
39          expiresIn: 3600
40        },
41        (err, token) => {
42          if (err) throw err;
43          res.status(200).json({
44            token
45          });
46        }
47      );
48    } catch (e) {
49      console.error(e);
50      res.status(500).json({
51        message: "Server Error"
52      });
53    }
54  }
55);

احراز هویت در Node.js

گام آخر

اکنون ثبت نام کاربر و ورود کاربر عملیاتی شده‌اند و در پاسخ یک توکن دریافت می‌کنید. بنابراین کار بعدی ما این است که کاربران وارد شده را با استفاده از توکن بازیابی کنیم. این کارکرد را نیز در ادامه اضافه می‌کنیم. مسیر user/me/ در صورتی که توکن در هدر ارسال شده باشد، کاربر مربوطه را بازگشت می‌دهد. در فایل route.js کد زیر را اضافه کنید:

1/**
2 * @method - POST
3 * @description - Get LoggedIn User
4 * @param - /user/me
5 */
6router.get("/me", auth, async (req, res) => {
7  try {
8    // request.user is getting fetched from Middleware after token authentication
9    const user = await User.findById(req.user.id);
10    res.json(user);
11  } catch (e) {
12    res.send({ message: "Error in Fetching user" });
13  }
14});

اکنون یک احراز هویت میان‌افزار اضافه می‌کنیم که می‌توانید در مسیر user/me/ ببینید. در ادامه پوشه‌ای به نام middleware می‌سازیم و درون آن فایلی به نام auth.js ایجاد می‌کنیم. میان‌افزار auth برای تأیید اعتبار توکن و بازیابی یک کاربر بر مبنای payload توکن مورد استفاده قرار می‌گیرد:

1const jwt = require("jsonwebtoken");
2module.exports = function(req, res, next) {
3  const token = req.header("token");
4  if (!token) return res.status(401).json({ message: "Auth Error" });
5  try {
6    const decoded = jwt.verify(token, "randomString");
7    req.user = decoded.user;
8    next();
9  } catch (e) {
10    console.error(e);
11    res.status(500).send({ message: "Invalid Token" });
12  }
13};

بدین ترتیب موفق شدیم API احراز هویت را در Node.js ایجاد کنیم. اینک می‌توانیم نقطه انتهایی /user/me را پس از وارد شدن تست کنیم.

اپلیکیشن را چگونه تست کنیم؟

Postman برای تست کردن API ضروری است. اگر Postman را نصب نکرده‌اید، ابتدا آن را نصب کنید.

  • ابتدا کاربر را ثبت کرده و یا در صورتی که قبلاً ثبت نام کرده است، او را وارد کنید.
  • در گام 1 یک توکن گرفته می‌شود. آن توکن را کپی کرده و در هدر قرار دهید.
  • روی submit کلیک کنید.

پیش‌نمایش تست به صورت زیر است:

احراز هویت در Node.js

سخن پایانی

در این مقاله به بررسی احراز هویت در Node.js با استفاده از Express ،JsonWebToken و MongoDB پرداختیم. بدین ترتیب با شیوه نوشتن میان‌افزار آشنا شدیم. سورس کد کامل این پروژه را می‌توانید در این ریپوی گیت‌هاب (+) ملاحظه کنید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
better-programming
۵ دیدگاه برای «پیاده سازی احراز هویت در Node.js با Express و JWT — از صفر تا صد»

سلام.
در این خط که const user = require(“./routes/user”) را ایمپورت کرده اید.چرا در این آدرس ماژولی اکسپورت نشده است؟و چرا Router اکسپرت نشده؟

ممنون.درست شد.
خیلی عالی بود

با سلام و احترام
ممنون از مقاله خوبتون
در قسمت لاگین مقدار کلید برای jwt متفاوت می باشد.که اشتباه است
اگر چه در کد گیت هاب درست نوشته شده است.
با تشکر

سلام و وقت بخیر؛
من همه کدها و بلکه محتوای مقاله را دوباره بازخوانی کردم. به طور خاص فایل لاگین را به دقت و خط‌به‌خط خواندم و حتی با منبع و همچنین ریپوی گیت‌هاب تطبیق دادم و متوجه نکته اشتباه یا متفاوتی نشدم. ای کاش مشکلی که اشاره می‌کنید را به طور دقیق‌تری بیان فرموده بودید.
از توجه شما بسیار سپاسگزارم.

واضح توضیح دادین
ممنونم

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *