البرمجة

إدارة الجلسات في Express

إنشاء مدونة باستخدام Express (الجزء 4): إدارة الجلسات

تُعد إدارة الجلسات (Session Management) من أهم الجوانب التقنية في بناء أي تطبيق ويب يعتمد على التفاعل مع المستخدمين، وخاصة في مشاريع مثل المدونات التي تتطلب تسجيل الدخول، وحفظ حالة المستخدم، وتحديد الصلاحيات، وغيرها من الأمور التي لا يمكن تحقيقها من دون منظومة جلسات قوية وآمنة. في هذا الجزء من سلسلة “إنشاء مدونة باستخدام Express”، يتم تناول موضوع إدارة الجلسات بشكل موسع، من الجوانب النظرية إلى التطبيق العملي.


ما هي الجلسة (Session) ولماذا تُستخدم؟

الجلسة هي مجموعة من البيانات المؤقتة المخزنة على الخادم ترتبط بمستخدم معين خلال فترة معينة من التفاعل مع التطبيق. تستخدم الجلسات لتخزين معلومات مثل هوية المستخدم، حالة تسجيل الدخول، الإعدادات الشخصية، أو أي معلومات أخرى يجب أن تبقى متاحة للمستخدم طوال فترة زيارته للتطبيق، دون الحاجة لإعادة إدخالها في كل مرة.

تختلف الجلسات عن ملفات تعريف الارتباط (Cookies) في أن الجلسة تُخزن غالبًا على الخادم، بينما يتم تخزين ملفات تعريف الارتباط على جهاز المستخدم. تُستخدم ملفات تعريف الارتباط عادة لحمل مُعرِّف الجلسة فقط، مما يحافظ على أمان المعلومات.


مكتبة express-session: المفهوم والتثبيت

يُعتبر express-session هو الخيار الأكثر شيوعًا لإدارة الجلسات في تطبيقات Express. توفر هذه الحزمة آلية لإنشاء وتخزين الجلسات، بالإضافة إلى إعدادات متقدمة مثل مدة الجلسة، خيارات التشفير، وسياسات التخزين.

تثبيت المكتبة:

bash
npm install express-session

إعدادها في تطبيق Express:

javascript
const 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:

bash
npm install connect-redis redis
javascript
const 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 }));

استخدام الجلسات لتسجيل الدخول

في تطبيق مدونة، تُستخدم الجلسات بشكل رئيسي لتتبع حالة تسجيل الدخول. إليك كيفية ربط الجلسة بعملية المصادقة:

نموذج مصادقة بسيط:

javascript
app.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('بيانات غير صحيحة'); } });

التحقق من الجلسة لحماية المسارات:

javascript
function isAuthenticated(req, res, next) { if (req.session.user) { return next(); } res.redirect('/login'); } app.get('/dashboard', isAuthenticated, (req, res) => { res.send('لوحة التحكم الخاصة بك'); });

إنهاء الجلسة وتسجيل الخروج

من الضروري إتاحة خيار تسجيل الخروج للمستخدمين عن طريق تدمير الجلسة الحالية.

javascript
app.get('/logout', (req, res) => { req.session.destroy(err => { if (err) { return res.send('حدث خطأ عند تسجيل الخروج'); } res.redirect('/'); }); });

الحماية والأمان في إدارة الجلسات

تتطلب إدارة الجلسات عدة تدابير أمنية لحماية التطبيق والمستخدمين من الهجمات المحتملة مثل:

1. تهيئة cookie بشكل آمن

javascript
cookie: { secure: true, // HTTPS فقط httpOnly: true, // لا يمكن الوصول إليها عبر JavaScript maxAge: 3600000 // مدة الجلسة ساعة واحدة }

2. منع التزوير عبر المواقع (CSRF)

يمكن دمج مكتبة مثل csurf للحماية من هجمات تزوير الطلبات:

bash
npm install csurf
javascript
const csrf = require('csurf'); app.use(csrf());

3. حماية من اختطاف الجلسات

من الأفضل تدوير معرّف الجلسة عند تسجيل الدخول:

javascript
req.session.regenerate(err => { // الجلسة الجديدة });

تنظيم جلسات متعددة الأدوار

عند تطوير مدونة تحتوي على أدوار مختلفة مثل مشرفين، كتّاب، وزوار، يمكن استخدام الجلسة لتخزين نوع المستخدم والتحقق من صلاحياته في كل مسار:

javascript
function 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 أو تطبيقات الجوال
الأمان مرتفع يتطلب حماية إضافية

حذف بيانات معينة من الجلسة دون تدميرها

أحيانًا نحتاج إلى حذف عنصر محدد من الجلسة دون التأثير على باقي القيم المخزنة:

javascript
delete req.session.user;

مراقبة الجلسات وتحليل الاستخدام

لأغراض التتبع وتحسين الأداء، من المفيد تسجيل معلومات عن الجلسات النشطة وعدد المستخدمين:

javascript
app.use((req, res, next) => { console.log(`User session ID: ${req.session.id}`); next(); });

أو حفظ هذه البيانات في سجلّات خارجية للمراقبة والتحليل لاحقًا.


إدارة الجلسات في بنية RESTful أو SPA

في حال استخدام React أو Angular في الواجهة الأمامية، يتم التعامل مع الجلسات عبر ملفات تعريف الارتباط. يجب التأكد من إرسال هذه الملفات مع كل طلب:

javascript
axios.get('/api/data', { withCredentials: true });

ويجب في المقابل إعداد السيرفر لدعم CORS وتمكين ملفات تعريف الارتباط:

javascript
const cors = require('cors'); app.use(cors({ origin: 'http://localhost:3000', credentials: true }));

مثال تطبيقي متكامل

يُظهر المثال التالي تطبيقًا مبسطًا لتسجيل الدخول باستخدام الجلسات:

javascript
const 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، فإن البنية المناسبة ستعزز من أداء واستقرار المدونة وتُسهّل عملية التوسع المستقبلي.


المراجع:

  1. Express Session Documentation – https://www.npmjs.com/package/express-session

  2. OWASP Session Management Cheat Sheet – https://cheatsheetseries.owasp.org