إنشاء مدونة باستخدام Express (الجزء الثالث): إنشاء نظام المستخدمين
في هذا الجزء من سلسلة إنشاء مدونة باستخدام إطار العمل Express في Node.js، نركز بشكل شامل على إنشاء نظام المستخدمين. يعتبر نظام المستخدمين هو العمود الفقري لأي تطبيق ويب يتضمن محتوى مخصصًا أو إدارة وصول أو تفاعل شخصي. من دون وجود بنية قوية للمستخدمين، لا يمكن تحقيق تجربة مخصصة أو آمنة للمستخدم النهائي. يتناول هذا المقال كل الجوانب البرمجية اللازمة لإنشاء هذا النظام، من إعداد قاعدة البيانات إلى إدارة المصادقة وتسجيل الدخول، مع التركيز على الأسس الأمنية والمعمارية الضرورية لبناء تطبيق احترافي.
إعداد قاعدة بيانات المستخدمين
لبداية قوية، يجب تحديد بنية المستخدم في قاعدة البيانات. في تطبيقات Node.js، غالبًا ما يتم استخدام MongoDB كقاعدة بيانات NoSQL، مع استخدام Mongoose كمكتبة لنمذجة البيانات.
نموذج المستخدم User Schema
jsconst mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
minlength: 3,
maxlength: 30
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
password: {
type: String,
required: true,
minlength: 6
},
createdAt: {
type: Date,
default: Date.now
}
});
// تشفير كلمة المرور قبل حفظها
UserSchema.pre('save', async function (next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
module.exports = mongoose.model('User', UserSchema);
إنشاء واجهات API للتسجيل وتسجيل الدخول
بمجرد إعداد النموذج، تكون الخطوة التالية هي إنشاء نقاط النهاية (API endpoints) للتسجيل وتسجيل الدخول. يتم ذلك باستخدام Express Router لضمان فصل المنطق وتنظيم الكود.
تسجيل مستخدم جديد
jsconst express = require('express');
const router = express.Router();
const User = require('../models/User');
const jwt = require('jsonwebtoken');
router.post('/register', async (req, res) => {
const { username, email, password } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) return res.status(400).json({ message: 'المستخدم موجود مسبقاً' });
const newUser = new User({ username, email, password });
await newUser.save();
const token = jwt.sign({ id: newUser._id }, process.env.JWT_SECRET, { expiresIn: '1d' });
res.status(201).json({
token,
user: {
id: newUser._id,
username: newUser.username,
email: newUser.email
}
});
} catch (error) {
res.status(500).json({ message: 'خطأ في الخادم' });
}
});
تسجيل الدخول
jsrouter.post('/login', async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) return res.status(404).json({ message: 'المستخدم غير موجود' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: 'كلمة المرور غير صحيحة' });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1d' });
res.json({
token,
user: {
id: user._id,
username: user.username,
email: user.email
}
});
} catch (error) {
res.status(500).json({ message: 'خطأ في الخادم' });
}
});
حماية المسارات باستخدام Middleware للمصادقة
لا بد من حماية بعض المسارات حتى لا يتم الوصول إليها إلا من قبل المستخدمين المسجلين. يتم ذلك من خلال إعداد دالة Middleware تقوم بفحص صحة التوكن JWT.
middleware للتحقق من التوكن
jsconst jwt = require('jsonwebtoken');
function authenticate(req, res, next) {
const token = req.header('Authorization');
if (!token) return res.status(401).json({ message: 'لا يوجد توكن، الدخول مرفوض' });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ message: 'توكن غير صالح' });
}
}
module.exports = authenticate;
تطبيق الحماية على مسار معين
jsconst authenticate = require('../middleware/authenticate');
router.get('/profile', authenticate, async (req, res) => {
try {
const user = await User.findById(req.user.id).select('-password');
res.json(user);
} catch (err) {
res.status(500).json({ message: 'خطأ في الخادم' });
}
});
بنية المجلدات لتنظيم الكود
يُستحسن استخدام بنية مجلدات واضحة تسهل صيانة وتطوير التطبيق مع الوقت:
pgsqlproject-root/
│
├── models/
│ └── User.js
├── routes/
│ └── auth.js
├── middleware/
│ └── authenticate.js
├── controllers/
│ └── authController.js (اختياري للفصل بين المنطق والمسارات)
├── config/
│ └── db.js (للاتصال بقاعدة البيانات)
├── .env
├── server.js
└── package.json
إعداد البيئة وملفات التكوين
يجب تخزين معلومات حساسة مثل مفاتيح JWT في ملف بيئة .env:
iniPORT=5000
JWT_SECRET=yourSuperSecretKey
MONGO_URI=mongodb://localhost:27017/express_blog
ويتم قراءتها في التطبيق باستخدام dotenv:
jsrequire('dotenv').config();
استخدام Postman لاختبار الواجهات
تُعد أداة Postman مثالية لاختبار نقاط النهاية:
| نوع الطلب | عنوان المسار | الوصف |
|---|---|---|
| POST | /api/auth/register |
تسجيل مستخدم جديد |
| POST | /api/auth/login |
تسجيل الدخول |
| GET | /api/auth/profile |
عرض ملف المستخدم (محمي) |
تخزين التوكن في العميل
بعد تسجيل الدخول بنجاح، يتم إرجاع توكن JWT للعميل. يمكن تخزينه في:
-
localStorage: جيد للجلسات الطويلة، لكنه معرض لهجمات XSS.
-
HTTP-only cookies: أكثر أمانًا، خاصة عند دمج Express مع React أو أي واجهة أمامية.
الجدول العام لبنية نظام المستخدمين
| المكون | الوصف |
|---|---|
| User Schema | يمثل نموذج المستخدم في قاعدة البيانات ويشمل التشفير المسبق لكلمة المرور |
| Register API | يسجل مستخدمًا جديدًا بعد التحقق من التكرار ويصدر توكن JWT |
| Login API | يتحقق من بيانات الدخول ويصدر توكن JWT للمستخدم |
| Auth Middleware | يتحقق من صلاحية التوكن ويحمي المسارات الخاصة |
| بنية المجلدات | تنظيم الكود في ملفات واضحة لتسهيل الصيانة |
| Postman | أداة لاختبار صحة واجهات API |
الجوانب الأمنية الإضافية
لضمان حماية المستخدمين وبياناتهم، ينبغي الأخذ بعين الاعتبار ما يلي:
-
تقييد محاولات الدخول (Rate Limiting): لمنع الهجمات من نوع brute-force.
-
التحقق من صحة المدخلات (Validation): باستخدام مكتبات مثل
express-validatorأوJoi. -
استخدام HTTPS: لتشفير البيانات المرسلة بين العميل والخادم.
-
تشفير كلمة المرور باستخدام Salt: كما يوفر bcrypt تلقائيًا.
-
إبطال الجلسات: عبر تعديل توقيت صلاحية التوكن أو استخدام قوائم سوداء.
التكامل مع أنظمة التعليقات أو المقالات
بعد إنشاء نظام المستخدمين، يمكن ربطه مع أنظمة أخرى في المدونة، مثل:
-
تحديد مؤلف المقال عبر معرف المستخدم.
-
السماح للمستخدمين بنشر تعليقات أو التفاعل مع المحتوى.
-
منح صلاحيات خاصة للمشرفين أو المدراء عبر نظام الأدوار.
الخلاصة المعمارية
يمثل نظام المستخدمين حجر الأساس في تطبيق المدونة المعتمد على Express، فهو يوفر قاعدة يمكن البناء عليها لتقديم وظائف أعمق مثل إنشاء المحتوى، تعديل المقالات، والتفاعل الاجتماعي. يتطلب النظام تفكيرًا عميقًا في الجوانب الأمنية والمعمارية لتقديم تجربة موثوقة وآمنة للمستخدمين. بالاعتماد على تقنيات حديثة مثل JWT وMongoose، يمكن تحقيق ذلك بكفاءة ومرونة عالية.
المراجع:

