المحطّة ٤: التسجيل والدخول — الجلسات وتعمية كلمة المرور
بوّابة المشروع: حسابٌ آمنٌ وجلسةٌ تعرف «مَن المستخدم الحاليّ». ⏱ نحو 28 دقيقة.
- تعمّي كلمات المرور بـ
password_hashوتتحقّق بـpassword_verify. - تفتح جلسةً بـ
session_startوتخزّن هويّة المستخدم في $_SESSION. - تبني تسجيلًا ودخولًا وخروجًا آمنًا متكاملًا.
- تتجنّب كشف أسباب الفشل ورسائل الخطأ المُسرِّبة.
١القاعدة الذهبيّة: لا تحفظ كلمة المرور أبدًا
كما تعلّمت في مقرّر 243، كلمة المرور الصريحة لا تُحفظ في القاعدة قطّ. لو سُرِّبت القاعدة انكشفت حسابات الجميع — وكثيرٌ منهم يستخدم الكلمة نفسها في بنكه وبريده.
الحلّ: نحفظ تعميةً (hash) لا يمكن عكسها. عند الدخول نعمّي ما أدخله المستخدم ونقارن التعميتين. PHP توفّر دالّتين جاهزتين آمنتين:
<?php$hash = password_hash('noura123', PASSWORD_DEFAULT); // عند التسجيل: نحفظ هذاecho $hash, "\n"; var_dump(password_verify('noura123', $hash)); // عند الدخول: صحيح؟var_dump(password_verify('wrong', $hash)); // كلمة خاطئة
٢الجلسة: كيف «يتذكّر» الموقع أنك داخل؟
HTTP بلا ذاكرة: كل طلبٍ منفصل. فكيف يعرف الخادم أنك المستخدم نفسه عبر الصفحات؟ عبر الجلسة (Session): عند الدخول نحفظ معرّف المستخدم في $_SESSION، ويرسل الخادم للمتصفّح كوكيًّا يحمله في كل طلبٍ لاحق.
<?phpsession_start(); // أوّل سطرٍ في كل صفحةٍ تستخدم الجلسة$_SESSION['uid'] = $user['id']; // عند نجاح الدخول: احفظ هويّة المستخدم// في أيّ صفحةٍ محميّة:if (!isset($_SESSION['uid'])) { echo 'سجّل الدخول أوّلًا'; exit; }
$_SESSION['uid'] هو مفتاح مشروعنا كلّه: به نعرف «مَن الطالب الحاليّ»، ومنه نجلب مهامّه هو فقط في المحطّة القادمة.
٣دخولٌ يعمل: من النموذج إلى الجلسة
نجرّب دورةً كاملة: نسجّل الدخول باسم sarah (موجودةٌ في بيانات التجربة، كلمتها sarah123)، ثم نفتح صفحةً محميّةً تقرأ الجلسة:
<?phpsession_start();require __DIR__ . '/db.php'; $st = $pdo->prepare('SELECT * FROM users WHERE username = ?');$st->execute([$_POST['username'] ?? '']);$user = $st->fetch(); if ($user && password_verify($_POST['password'] ?? '', $user['pass_hash'])) { $_SESSION['uid'] = $user['id']; $_SESSION['username'] = $user['username']; echo "مرحبًا {$user['username']}";} else { echo "بيانات غير صحيحة";}
<?phpsession_start();echo isset($_SESSION['username']) ? "أنت: {$_SESSION['username']}" : "لم تسجّل الدخول";
<?php$pdo = new PDO('sqlite:' . __DIR__ . '/tasks.sqlite');$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
لاحظ: الطلب الثاني (me.php) عرف المستخدم دون إرسال كلمة مرورٍ — لأن الكوكي يحمل الجلسة. والطلب الثالث بكلمةٍ خاطئةٍ رُفِض.
توقّع وتحقّق
كيف نحفظ كلمة المرور بأمان؟
ما وظيفة session_start()؟
عند فشل الدخول، ما الرسالة الأأمن؟
أين نخزّن هويّة المستخدم بعد نجاح الدخول؟
الخلاصة
- لا تحفظ كلمة المرور أبدًا؛ احفظ تعميتها بـ
password_hash (bcrypt)وتحقّق بـpassword_verify. session_startفي أعلى كل صفحةٍ قبل أيّ مخرَج؛ الجلسة تربط الطلبات بمستخدمٍ واحد.- $
_SESSION['uid']هو هويّة المستخدم الحاليّ — أساس عزل البيانات لاحقًا. - عند الفشل قل «بيانات غير صحيحة» دون كشف أيّ الحقلين أخطأ.
- تحقّق من عدم تكرار اسم المستخدم، ومن أنّ الحقول غير فارغة.
تحدٍّ برمجي: التسجيل والدخول
برمجيابنِ مصادقة مشروعك: صفحة تسجيلٍ تنشئ حسابًا بكلمة مرورٍ مُعمّاة، وصفحة دخولٍ تتحقّق وتفتح جلسة، وصفحة تعرف المستخدم الحاليّ، وخروجٌ ينهي الجلسة.
register.php(POST): يعمّي كلمة المرور بـpassword_hash، يُدرج المستخدم، يفتح الجلسة، ويطبعتم إنشاء الحساب: الاسم.login.php(POST): يتحقّق بـpassword_verify؛ عند النجاح يطبعمرحبًا الاسم، وإلّابيانات غير صحيحة.me.php(GET): يطبعأنت: الاسمإن كان داخلًا، وإلّازائر.logout.php(GET): ينهي الجلسة ويطبعخرجت.
👀 اعرض الاستجابة المطلوبة
POST /register.php(username=noura&password=noura123)GET /me.phpGET /logout.phpGET /me.phpPOST /login.php(username=noura&password=noura123)POST /login.php(username=noura&password=wrong)
- الكود يعمل بلا أخطاء أو تحذيرات
- التسجيل يعمّي ويُنشئ الحساب
- الجلسة تتذكّر المستخدم بعد التسجيل
- الخروج ثم me يعيد «زائر»
- الدخول بكلمةٍ صحيحةٍ ينجح
- الدخول بكلمةٍ خاطئةٍ يُرفَض برسالةٍ محايدة
- لا يكشف كلمة المرور في أيّ ردّ
💡 تلميح 1
💡 تلميح 2
_POST[...], $user["pass_hash"]).💡 تلميح 3
session_destroy()؛ وصفحة me تقرأ $_SESSION["username"].حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
<?phpsession_start();require __DIR__ . '/db.php'; $u = trim($_POST['username'] ?? '');$p = $_POST['password'] ?? '';$hash = password_hash($p, PASSWORD_DEFAULT); $st = $pdo->prepare('INSERT INTO users (username, pass_hash, created_at) VALUES (?, ?, ?)');$st->execute([$u, $hash, date('Y-m-d')]); $_SESSION['uid'] = $pdo->lastInsertId();$_SESSION['username'] = $u;echo "تم إنشاء الحساب: {$u}";
<?phpsession_start();require __DIR__ . '/db.php'; $st = $pdo->prepare('SELECT * FROM users WHERE username = ?');$st->execute([$_POST['username'] ?? '']);$user = $st->fetch(); if ($user && password_verify($_POST['password'] ?? '', $user['pass_hash'])) { $_SESSION['uid'] = $user['id']; $_SESSION['username'] = $user['username']; echo "مرحبًا {$user['username']}";} else { echo "بيانات غير صحيحة";}
<?phpsession_start();echo isset($_SESSION['username']) ? "أنت: {$_SESSION['username']}" : "زائر";
<?phpsession_start();session_destroy();echo "خرجت";
<?php$pdo = new PDO('sqlite:' . __DIR__ . '/tasks.sqlite');$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);