أساسيات PHP
نظام تسجيل دخول المستخدمين
نظام تسجيل دخول المستخدمين
نظام تسجيل الدخول الآمن ضروري لمصادقة المستخدمين وإدارة جلساتهم. في هذا الدرس، سنبني نظام تسجيل دخول كامل مع التحقق من كلمة المرور، إدارة الجلسات، وميزات الأمان مثل تذكرني وقفل الحساب.
نموذج تسجيل الدخول HTML
لنقم بإنشاء نموذج تسجيل دخول احترافي بتصميم حديث:
login.php:
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تسجيل الدخول</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Cairo', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
max-width: 450px;
width: 100%;
}
h1 {
color: #333;
margin-bottom: 10px;
text-align: center;
}
.subtitle {
text-align: center;
color: #777;
margin-bottom: 30px;
font-size: 14px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 16px;
transition: border-color 0.3s;
direction: rtl;
}
input:focus {
outline: none;
border-color: #667eea;
}
.checkbox-group {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
font-size: 14px;
}
.checkbox-group label {
display: flex;
align-items: center;
font-weight: normal;
cursor: pointer;
}
.checkbox-group input[type="checkbox"] {
margin-left: 5px;
cursor: pointer;
}
.forgot-password {
color: #667eea;
text-decoration: none;
transition: color 0.3s;
}
.forgot-password:hover {
color: #5568d3;
text-decoration: underline;
}
.error-message {
background: #fee;
color: #c33;
padding: 12px;
border-radius: 5px;
margin-bottom: 20px;
border-right: 4px solid #c33;
}
.success-message {
background: #efe;
color: #3c3;
padding: 12px;
border-radius: 5px;
margin-bottom: 20px;
border-right: 4px solid #3c3;
}
button {
width: 100%;
padding: 12px;
background: #667eea;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #5568d3;
}
.register-link {
text-align: center;
margin-top: 20px;
color: #555;
}
.register-link a {
color: #667eea;
text-decoration: none;
font-weight: bold;
}
</style>
</head>
<body>
<div class="login-container">
<h1>مرحباً بعودتك</h1>
<p class="subtitle">يرجى تسجيل الدخول إلى حسابك</p>
<?php if (isset($error_message)): ?>
<div class="error-message"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if (isset($success_message)): ?>
<div class="success-message"><?php echo htmlspecialchars($success_message); ?></div>
<?php endif; ?>
<form method="POST" action="login.php" id="loginForm">
<div class="form-group">
<label for="username">اسم المستخدم أو البريد الإلكتروني</label>
<input type="text" id="username" name="username"
value="<?php echo htmlspecialchars($username ?? ''); ?>"
required autofocus>
</div>
<div class="form-group">
<label for="password">كلمة المرور</label>
<input type="password" id="password" name="password" required>
</div>
<div class="checkbox-group">
<label>
<input type="checkbox" name="remember_me" value="1">
تذكرني
</label>
<a href="forgot-password.php" class="forgot-password">نسيت كلمة المرور؟</a>
</div>
<button type="submit">تسجيل الدخول</button>
</form>
<div class="register-link">
ليس لديك حساب؟ <a href="register.php">سجل هنا</a>
</div>
</div>
</body>
</html>
منطق تسجيل الدخول مع ميزات الأمان
تنفيذ منطق تسجيل دخول آمن مع التحقق من كلمة المرور وإدارة الجلسات:
login.php (منطق PHP في الأعلى):
<?php
session_start();
// إعدادات قاعدة البيانات
$host = 'localhost';
$dbname = 'your_database';
$db_username = 'your_username';
$db_password = 'your_password';
// تهيئة المتغيرات
$error_message = '';
$success_message = '';
$username = '';
// إنشاء اتصال بقاعدة البيانات
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $db_username, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("فشل الاتصال: " . $e->getMessage());
}
// التحقق من تسجيل الدخول مسبقاً
if (isset($_SESSION['user_id'])) {
header('Location: dashboard.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$remember_me = isset($_POST['remember_me']);
// التحقق من المدخلات
if (empty($username)) {
$error_message = 'يرجى إدخال اسم المستخدم أو البريد الإلكتروني';
} elseif (empty($password)) {
$error_message = 'يرجى إدخال كلمة المرور';
} else {
// التحقق من محاولات تسجيل الدخول
$ip_address = $_SERVER['REMOTE_ADDR'];
$attempts = getLoginAttempts($pdo, $ip_address);
if ($attempts >= 5) {
$error_message = 'عدد كبير من محاولات تسجيل الدخول الفاشلة. يرجى المحاولة مرة أخرى بعد 15 دقيقة.';
} else {
// البحث عن المستخدم
$stmt = $pdo->prepare('
SELECT id, username, email, password, full_name, is_active
FROM users
WHERE username = ? OR email = ?
LIMIT 1
');
$stmt->execute([$username, $username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
if (!$user['is_active']) {
$error_message = 'تم تعطيل حسابك. يرجى الاتصال بالدعم.';
logLoginAttempt($pdo, $ip_address, $username, false);
}
elseif (password_verify($password, $user['password'])) {
// تسجيل دخول ناجح
// إعادة إنشاء معرف الجلسة
session_regenerate_id(true);
// تعيين متغيرات الجلسة
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['full_name'] = $user['full_name'];
$_SESSION['email'] = $user['email'];
$_SESSION['logged_in_at'] = time();
// تحديث وقت آخر تسجيل دخول
$stmt = $pdo->prepare('UPDATE users SET last_login = NOW() WHERE id = ?');
$stmt->execute([$user['id']]);
// معالجة "تذكرني"
if ($remember_me) {
setRememberMeCookie($pdo, $user['id']);
}
// مسح محاولات تسجيل الدخول الفاشلة
clearLoginAttempts($pdo, $ip_address);
// تسجيل محاولة تسجيل الدخول الناجحة
logLoginAttempt($pdo, $ip_address, $username, true);
// إعادة التوجيه إلى لوحة التحكم
header('Location: dashboard.php');
exit;
} else {
$error_message = 'اسم المستخدم أو كلمة المرور غير صحيحة';
logLoginAttempt($pdo, $ip_address, $username, false);
}
} else {
$error_message = 'اسم المستخدم أو كلمة المرور غير صحيحة';
logLoginAttempt($pdo, $ip_address, $username, false);
}
}
}
}
function getLoginAttempts($pdo, $ip_address) {
$stmt = $pdo->prepare('
SELECT COUNT(*) FROM login_attempts
WHERE ip_address = ?
AND success = 0
AND attempted_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
');
$stmt->execute([$ip_address]);
return (int) $stmt->fetchColumn();
}
function logLoginAttempt($pdo, $ip_address, $username, $success) {
$stmt = $pdo->prepare('
INSERT INTO login_attempts (ip_address, username, success, attempted_at)
VALUES (?, ?, ?, NOW())
');
$stmt->execute([$ip_address, $username, $success ? 1 : 0]);
}
function clearLoginAttempts($pdo, $ip_address) {
$stmt = $pdo->prepare('DELETE FROM login_attempts WHERE ip_address = ?');
$stmt->execute([$ip_address]);
}
function setRememberMeCookie($pdo, $user_id) {
$token = bin2hex(random_bytes(32));
$hashed_token = hash('sha256', $token);
$stmt = $pdo->prepare('
INSERT INTO remember_tokens (user_id, token, expires_at)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY))
');
$stmt->execute([$user_id, $hashed_token]);
setcookie('remember_me', $token, [
'expires' => time() + (30 * 24 * 60 * 60),
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
}
?>
تحذير أمني: استخدم دائماً
password_verify() للتحقق من كلمات المرور، لا تقارن كلمات المرور المشفرة مباشرة. هذه الدالة مصممة لتكون آمنة ضد هجمات التوقيت.
وظيفة تسجيل الخروج
إنشاء سكريبت تسجيل خروج آمن يمسح الجلسات والكوكيز:
logout.php:
<?php
session_start();
require_once 'db_connect.php';
// إزالة رمز "تذكرني" إذا كان موجوداً
if (isset($_COOKIE['remember_me'])) {
$token = $_COOKIE['remember_me'];
$hashed_token = hash('sha256', $token);
$stmt = $pdo->prepare('DELETE FROM remember_tokens WHERE token = ?');
$stmt->execute([$hashed_token]);
setcookie('remember_me', '', time() - 3600, '/', '', true, true);
}
// إلغاء تعيين جميع متغيرات الجلسة
$_SESSION = [];
// تدمير كوكي الجلسة
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 3600, '/');
}
// تدمير الجلسة
session_destroy();
// إعادة التوجيه إلى صفحة تسجيل الدخول
header('Location: login.php?logout=1');
exit;
?>
نصيحة احترافية: أعد دائماً إنشاء معرفات الجلسة بعد تسجيل الدخول باستخدام
session_regenerate_id(true) لمنع هجمات تثبيت الجلسة. المعامل true يحذف ملف الجلسة القديم.
تمرين:
- أنشئ نموذج تسجيل الدخول ونفذ التحقق من كلمة المرور
- أضف جدول login_attempts ونفذ تقييد المعدل
- اختبر وظيفة "تذكرني" بتسجيل الدخول وإغلاق المتصفح
- نفذ ميزة "نسيت كلمة المرور" مع التحقق من البريد الإلكتروني
- أضف مصادقة ثنائية (2FA) باستخدام رموز البريد الإلكتروني أو SMS
- أنشئ لوحة إدارة لعرض وإدارة محاولات تسجيل الدخول
ملخص أفضل الممارسات
- استخدم password_verify() للتحقق من كلمات المرور بشكل آمن
- أعد إنشاء معرفات الجلسة بعد تسجيل الدخول الناجح
- نفذ تقييد المعدل لمنع هجمات القوة الغاشمة
- تحقق من الجلسات باستخدام وكيل المستخدم ومهلات النشاط
- استخدم كوكيز آمنة مع سمات httponly و secure و samesite
- شفر رموز التذكر قبل التخزين في قاعدة البيانات
- امسح الجلسات بشكل صحيح عند تسجيل الخروج