إنشاء مدونة باستخدام Express (الجزء 4): إدارة الجلسات
تُعد إدارة الجلسات (Session Management) من أهم الجوانب التقنية في بناء أي تطبيق ويب يعتمد على التفاعل مع المستخدمين، وخاصة في مشاريع مثل المدونات التي تتطلب تسجيل الدخول، وحفظ حالة المستخدم، وتحديد الصلاحيات، وغيرها من الأمور التي لا يمكن تحقيقها من دون منظومة جلسات قوية وآمنة. في هذا الجزء من سلسلة “إنشاء مدونة باستخدام Express”، يتم تناول موضوع إدارة الجلسات بشكل موسع، من الجوانب النظرية إلى التطبيق العملي.
ما هي الجلسة (Session) ولماذا تُستخدم؟
الجلسة هي مجموعة من البيانات المؤقتة المخزنة على الخادم ترتبط بمستخدم معين خلال فترة معينة من التفاعل مع التطبيق. تستخدم الجلسات لتخزين معلومات مثل هوية المستخدم، حالة تسجيل الدخول، الإعدادات الشخصية، أو أي معلومات أخرى يجب أن تبقى متاحة للمستخدم طوال فترة زيارته للتطبيق، دون الحاجة لإعادة إدخالها في كل مرة.
تختلف الجلسات عن ملفات تعريف الارتباط (Cookies) في أن الجلسة تُخزن غالبًا على الخادم، بينما يتم تخزين ملفات تعريف الارتباط على جهاز المستخدم. تُستخدم ملفات تعريف الارتباط عادة لحمل مُعرِّف الجلسة فقط، مما يحافظ على أمان المعلومات.
مكتبة express-session: المفهوم والتثبيت
يُعتبر express-session هو الخيار الأكثر شيوعًا لإدارة الجلسات في تطبيقات Express. توفر هذه الحزمة آلية لإنشاء وتخزين الجلسات، بالإضافة إلى إعدادات متقدمة مثل مدة الجلسة، خيارات التشفير، وسياسات التخزين.
تثبيت المكتبة:
bashnpm install express-session
إعدادها في تطبيق Express:
javascriptconst session = require('express-session');
const express = require('express');
const app = express();
app.use(session({
secret: 'secret-key',
resave: false,
saveUninitialized: false,
cookie: { secure: false } // true في حالة HTTPS فقط
}));
-
secret: مفتاح سري يُستخدم لتوقيع معرّف الجلسة.
-
resave: تحديد ما إذا كان يجب حفظ الجلسة في كل طلب حتى وإن لم تتغير.
-
saveUninitialized: تحديد ما إذا كان يجب حفظ الجلسات الجديدة غير المعدلة.
-
cookie.secure: إذا كانت الجلسة تتطلب HTTPS فقط.
تخزين الجلسات: الذاكرة مقابل قواعد البيانات
بشكل افتراضي، تُخزن الجلسات في الذاكرة (MemoryStore)، وهذا مناسب فقط لتطبيقات التطوير أو التجريب، لأنه لا يدعم التوسّع ولا يضمن الاستقرار في البيئات الإنتاجية.
حلول بديلة للتخزين:
| نوع التخزين | الوصف | الحالة المناسبة |
|---|---|---|
| MemoryStore | التخزين في الذاكرة | بيئة التطوير فقط |
| Redis | تخزين عالي الأداء | التطبيقات الكبيرة |
| MongoDB | تخزين جلسات كوثائق | عندما تستخدم MongoDB للتطبيق |
| MySQL | تخزين على قواعد البيانات العلائقية | نظم تعتمد على قواعد علائقية |
مثال على تخزين الجلسة باستخدام Redis:
bashnpm install connect-redis redis
javascriptconst RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient();
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'secret-key',
resave: false,
saveUninitialized: false
}));
استخدام الجلسات لتسجيل الدخول
في تطبيق مدونة، تُستخدم الجلسات بشكل رئيسي لتتبع حالة تسجيل الدخول. إليك كيفية ربط الجلسة بعملية المصادقة:
نموذج مصادقة بسيط:
javascriptapp.post('/login', (req, res) => {
const { username, password } = req.body;
// تحقق من قاعدة البيانات
if (username === 'admin' && password === '1234') {
req.session.user = { name: 'admin' };
res.redirect('/dashboard');
} else {
res.send('بيانات غير صحيحة');
}
});
التحقق من الجلسة لحماية المسارات:
javascriptfunction isAuthenticated(req, res, next) {
if (req.session.user) {
return next();
}
res.redirect('/login');
}
app.get('/dashboard', isAuthenticated, (req, res) => {
res.send('لوحة التحكم الخاصة بك');
});
إنهاء الجلسة وتسجيل الخروج
من الضروري إتاحة خيار تسجيل الخروج للمستخدمين عن طريق تدمير الجلسة الحالية.
javascriptapp.get('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
return res.send('حدث خطأ عند تسجيل الخروج');
}
res.redirect('/');
});
});
الحماية والأمان في إدارة الجلسات
تتطلب إدارة الجلسات عدة تدابير أمنية لحماية التطبيق والمستخدمين من الهجمات المحتملة مثل:
1. تهيئة cookie بشكل آمن
javascriptcookie: {
secure: true, // HTTPS فقط
httpOnly: true, // لا يمكن الوصول إليها عبر JavaScript
maxAge: 3600000 // مدة الجلسة ساعة واحدة
}
2. منع التزوير عبر المواقع (CSRF)
يمكن دمج مكتبة مثل csurf للحماية من هجمات تزوير الطلبات:
bashnpm install csurf
javascriptconst csrf = require('csurf');
app.use(csrf());
3. حماية من اختطاف الجلسات
من الأفضل تدوير معرّف الجلسة عند تسجيل الدخول:
javascriptreq.session.regenerate(err => {
// الجلسة الجديدة
});
تنظيم جلسات متعددة الأدوار
عند تطوير مدونة تحتوي على أدوار مختلفة مثل مشرفين، كتّاب، وزوار، يمكن استخدام الجلسة لتخزين نوع المستخدم والتحقق من صلاحياته في كل مسار:
javascriptfunction isAdmin(req, res, next) {
if (req.session.user && req.session.user.role === 'admin') {
return next();
}
res.status(403).send('ممنوع الدخول');
}
الجلسات مقابل JWT: متى نستخدم كل منهما؟
في بعض الأحيان يُطرح التساؤل حول استخدام الجلسات (Sessions) أو رموز الوصول مثل JWT. الجلسات مناسبة للتطبيقات التي تُخزن حالتها على الخادم، أما JWT فتُستخدم أكثر في التطبيقات التي تعتمد على APIs.
| المعيار | الجلسة session |
JWT |
|---|---|---|
| التخزين | على الخادم | على العميل (عادة في LocalStorage) |
| قابلية الإبطال | سهلة | معقدة |
| مناسب لـ | تطبيقات تقليدية | SPA أو تطبيقات الجوال |
| الأمان | مرتفع | يتطلب حماية إضافية |
حذف بيانات معينة من الجلسة دون تدميرها
أحيانًا نحتاج إلى حذف عنصر محدد من الجلسة دون التأثير على باقي القيم المخزنة:
javascriptdelete req.session.user;
مراقبة الجلسات وتحليل الاستخدام
لأغراض التتبع وتحسين الأداء، من المفيد تسجيل معلومات عن الجلسات النشطة وعدد المستخدمين:
javascriptapp.use((req, res, next) => {
console.log(`User session ID: ${req.session.id}`);
next();
});
أو حفظ هذه البيانات في سجلّات خارجية للمراقبة والتحليل لاحقًا.
إدارة الجلسات في بنية RESTful أو SPA
في حال استخدام React أو Angular في الواجهة الأمامية، يتم التعامل مع الجلسات عبر ملفات تعريف الارتباط. يجب التأكد من إرسال هذه الملفات مع كل طلب:
javascriptaxios.get('/api/data', { withCredentials: true });
ويجب في المقابل إعداد السيرفر لدعم CORS وتمكين ملفات تعريف الارتباط:
javascriptconst cors = require('cors');
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
مثال تطبيقي متكامل
يُظهر المثال التالي تطبيقًا مبسطًا لتسجيل الدخول باستخدام الجلسات:
javascriptconst express = require('express');
const session = require('express-session');
const app = express();
app.use(express.json());
app.use(session({
secret: 'blog-secret',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: false,
maxAge: 3600000
}
}));
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === 'admin' && password === '1234') {
req.session.user = { name: 'admin', role: 'admin' };
res.json({ message: 'تم تسجيل الدخول بنجاح' });
} else {
res.status(401).json({ message: 'بيانات غير صحيحة' });
}
});
app.get('/dashboard', (req, res) => {
if (!req.session.user) {
return res.status(403).send('غير مصرح');
}
res.json({ message: `مرحبًا ${req.session.user.name}` });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
جدول مقارنة طرق إدارة الجلسات
| المعيار | express-session |
JWT |
|---|---|---|
| التخزين | على الخادم | على العميل |
| الحماية من الاختطاف | عالية باستخدام httpOnly |
تحتاج تدابير إضافية |
| سهولة الإبطال | سهلة | تحتاج تعقيد في التهيئة |
| الأداء في التوسعة | يحتاج تحسين مع Redis/Mongo | أفضل إذا كانت Stateless |
| مناسب لتطبيقات | تقليدية | SPAs أو APIs الخارجية |
الخاتمة
إدارة الجلسات عنصر أساسي لا يمكن الاستغناء عنه في تطوير أي تطبيق يعتمد على المستخدمين، وتُعد مكتبة express-session أداة موثوقة وفعالة لهذا الغرض. من خلال دمجها بشكل صحيح وتفعيل تدابير الحماية اللازمة، يمكن بناء بنية قوية وآمنة تضمن تجربة سلسة وآمنة للمستخدمين. سواء اخترت تخزين الجلسات على الخادم أو في أنظمة خارجية مثل Redis أو MongoDB، فإن البنية المناسبة ستعزز من أداء واستقرار المدونة وتُسهّل عملية التوسع المستقبلي.
المراجع:

