ساخت سیستم لاگین و ثبت نام با Node.js
در این مقاله قدمبهقدم یک سیستم ثبتنام و لاگین (احراز هویت) با Node.js را بررسی میکنیم. از طراحی مدل کاربر تا ذخیره ایمن رمزعبور، ایجاد توکن JWT، اعتبارسنجی ورودی و نکات امنیتی مهم را پوشش میدهیم. این راهنما برای توسعهدهندگان وب که میخواهند یک سیستم احراز هویت کاربردی و امن بسازند مناسب است.
پیشنیازها و انتخابها
- Node.js و npm
- Express برای سرور HTTP
- MongoDB + Mongoose برای ذخیره کاربر (میتوان با SQL هم پیاده کرد)
- bcrypt برای هش کردن رمزعبور
- jsonwebtoken برای تولید JWT
- express-validator یا Joi برای اعتبارسنجی ورودی
معماری ساده
ما از ساختار RESTful استفاده میکنیم: دو مسیر اصلی /api/auth/register و /api/auth/login. پس از لاگین، سرویس یک JWT به فرانتاند بازمیگرداند یا از سشن (session) استفاده میکند.
نمونه مدل کاربر با Mongoose
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
isVerified: { type: Boolean, default: false }
});
module.exports = mongoose.model('User', userSchema);
این مدل فیلدهای پایه را تعریف میکند: نام، ایمیل، رمزعبور، تاریخ ایجاد و فلگ تایید ایمیل. دقت کنید که ایمیل unique است تا چند ثبتنام با یک ایمیل رخ ندهد.
ثبتنام — ثبت امن رمزعبور
const express = require('express');
const bcrypt = require('bcrypt');
const User = require('./models/User');
const router = express.Router();
router.post('/register', async (req, res) => {
try {
const { name, email, password } = req.body;
const existing = await User.findOne({ email });
if (existing) return res.status(400).json({ message: 'Email already in use' });
const saltRounds = 12;
const hashed = await bcrypt.hash(password, saltRounds);
const user = new User({ name, email, password: hashed });
await user.save();
res.status(201).json({ message: 'User created' });
} catch (err) {
res.status(500).json({ message: 'Server error' });
}
});
module.exports = router;
کد بالا ابتدا بررسی میکند ایمیل تکراری وجود ندارد، سپس با bcrypt و 12 راند نمکزنی رمز را هش کرده و کاربر را ذخیره میکند. استفاده از saltRounds بالاتر امنیت را بهتر میکند ولی زمان را افزایش میدهد.
ورود و تولید JWT
const jwt = require('jsonwebtoken');
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) return res.status(400).json({ message: 'Invalid credentials' });
const match = await bcrypt.compare(password, user.password);
if (!match) return res.status(400).json({ message: 'Invalid credentials' });
const payload = { id: user._id, email: user.email };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (err) {
res.status(500).json({ message: 'Server error' });
}
});
در این بخش، اعتبارسنجی رمز با bcrypt.compare انجام میشود. سپس یک JWT با payload ساده ساخته و برگشت داده میشود. کلید مخفی (JWT_SECRET) باید در متغیر محیطی ذخیره شود و هرگز در کد منبع قرار نگیرد.
میانیافزار محافظت از مسیرها
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) return res.status(401).json({ message: 'No token' });
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ message: 'Invalid token' });
}
}
این middleware توکن را از هدر Authorization دریافت میکند، آن را بررسی میکند و در صورت معتبر بودن داده کاربر را به req.user اضافه و اجازه ادامه میدهد.
بهینهسازیها و نکات امنیتی
- استفاده از HTTPS در محیط تولید
- ذخیره secret در متغیر محیطی و استفاده از مدیریت کلید
- اعمال Rate Limiting برای جلوگیری از حملات بروتفورس (مثلاً express-rate-limit)
- استفاده از Helmet برای تنظیم هدرهای امنیتی
- اعتبارسنجی ورودیها با Joi یا express-validator برای جلوگیری از حملات تزریق
- در صورت نیاز به logout، برای JWT میتوان از لیست سیاه (blacklist) یا کوتاه کردن زمان انقضا استفاده کرد
- برای تایید ایمیل از ارسال لینک تایید استفاده کنید که حاوی توکن یکبارمصرف باشد
مقایسه کوتاه: JWT vs Session
| ویژگی | JWT | Session |
|---|---|---|
| مقیاسپذیری | خوب، بدون ذخیره سیشن سمت سرور | نیاز به ذخیرهسازی سیشن (Redis) |
| امنیت | نیاز به مدیریت blacklist برای logout | قابل کنترل و حذف سمت سرور |
| استفاده در موبایل | مناسب (token-based) | نیاز به مکانیزم کوکی/سشن |
نمونه بهبود: اعتبارسنجی با express-validator
const { body, validationResult } = require('express-validator');
router.post('/register', [
body('email').isEmail(),
body('password').isLength({ min: 8 })
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
// ادامه منطق ثبتنام...
});
این قطعه بررسی میکند ایمیل معتبر است و رمز حداقل 8 کاراکتر دارد. اعتبارسنجی درست جلوی دادههای نامناسب و حملات را میگیرد.
مثال استفاده واقعی و موارد کاربرد
این سیستم برای سایتهای مبتنی بر کاربر مانند شبکههای اجتماعی، فروشگاههای آنلاین و داشبوردهای تحت وب مناسب است. در اپلیکیشنهای موبایل نیز معمولاً از JWT استفاده میشود تا پس از لاگین کاربر توکن را در کلاینت ذخیره و در درخواستها ارسال کند.
خلاصه و نکات نهایی
برای ساخت یک سیستم ثبتنام و لاگین با Node.js توجه به امنیت (هش کردن رمز، HTTPS، مدیریت کلید)، اعتبارسنجی ورودی، و انتخاب مناسب بین JWT و سشن ضروری است. افزون بر این، پیادهسازی امکاناتی مانند تایید ایمیل، بازنشانی رمز (password reset) و محافظت در برابر حملات بروتفورس و CSRF تجربه کاربری و امنیت را بهبود میبخشد.
در نهایت، تستهای واحد و انتشار لاگهای مناسب برای تحلیل مشکلات در محیط تولید نقش مهمی در پایداری سیستم دارند.
آیا این مطلب برای شما مفید بود ؟




