في عصر الويب الحديث، أصبحت سرعة وسلاسة تجربة المستخدم (User Experience) هي الفاصل بين نجاح الموقع الإلكتروني أو مغادرة الزائر له. تُشير الدراسات الرقمية إلى أن أكثر من 70% من المستخدمين يفضلون استخدام ميزة تسجيل الدخول الاجتماعي (Social Login) مثل Google Sign-In بدلاً من ملء استمارات التسجيل التقليدية وتذكر كلمات مرور جديدة لكل موقع.
في هذا المقال التفصيلي على منصة HostStag، سنشرح خطوة بخطوة كيفية إنشاء نظام تسجيل دخول ذكي وآمن بالكامل يعتمد على أحدث مكتبات جوجل (Google Identity Services API)، مع ربطه بقاعدة البيانات باستخدام الاستعلامات الجاهزة (MySQLi Prepared Statements) لضمان أعلى مستويات الحماية والأداء.
لماذا نستخدم الاستعلامات الجاهزة (MySQLi Prepared Statements)؟
عند تعامل الموقع مع بيانات قادمة من مصادر خارجية أو من المستخدم مباشرة، تُصبح الاستعلامات التقليدية خطراً أمنياً كبيراً يُعرض الموقع لهجمات حقن قواعد البيانات (SQL Injection).
تتميز الاستعلامات الجاهزة (Prepared Statements) في مكتبة MySQLi بما يلي:
-
فصل الكود عن البيانات: يتم إرسال هيكل استعلام SQL إلى خادم قاعدة البيانات أولاً ليتم تحليله وتجهيزه، ثم تُرسل القيم والبيانات بشكل منفصل ومُشفر، مما يجعل من المستحيل على أي كود خبيث تغيير بنية الاستعلام.
-
تحسين الأداء: في حال تكرار تنفيذ الاستعلام (مثل البحث ثم التحديث أو الإدراج)، يتم تحضير الاستعلام مرة واحدة فقط وتنفيذه بسرعة فائقة.
-
التوافق التام مع بيئة PHP: تُعتبر محركاً معياريًا وخفيف الاستهلاك للذاكرة على خوادم الاستضافة.
إنشاء وتجهيز تطبيق جوجل (Google Cloud Console)
لتمكين موقعك من التواصل مع سيرفرات جوجل والتحقق من هوية الزائر، يجب الحصول على معرف العميل (Client ID) من منصة تطوير جوجل:
-
الوصول للمنصة: اذهب إلى منصة Google Cloud Console وسجل الدخول بحسابك.
-
إنشاء مشروع جديد: اضغط على القائمة العلوية ثم اختر New Project وقم بتسميته مثلاً:
HostStag Authentication. -
ضبط شاشة الموافقة (OAuth Consent Screen):
-
من القائمة الجانبية، اختر APIs & Services ثم OAuth consent screen.
-
حدد نوع المستخدم External ثم اضغط Create.
-
أدخل اسم تطبيقك (مثل
HostStag) والبريد الإلكتروني المخصص للدعم الفني، ثم أكمل الخطوات واضغط Save and Continue.
-
-
استخراج مفاتيح الاعتماد (Credentials):
-
انتقل إلى تبويب Credentials واضغط Create Credentials ثم اختر OAuth client ID.
-
اختر Web application في نوع التطبيق.
-
في خانة Authorized JavaScript origins: ضع رابط موقعك (مثال:
https://hoststag.comأوhttp://localhost). -
في خانة Authorized redirect URIs: أدخل رابط صفحة معالجة الدخول (مثال:
https://hoststag.com/google_callback.php).
-
-
حفظ المعرّف: بعد الضغط على Create، سيظهر لك Client ID. انسخه وحافظ عليه لنستخدمه بداخل كود הـ PHP.
تصميم قاعدة البيانات (MySQL Database)
نحتاج لجدول بسيط ومحكم لتخزين بيانات الزوار المسجلين عن طريق جوجل. سنقوم بإنشاء قيود فريدة (UNIQUE) على حقل google_id وحقل email لمنع تكرار الحسابات.
افتح أداة phpMyAdmin وأنفّذ استعلام SQL التالي:
CREATE DATABASE IF NOT EXISTS `hoststag_demo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `hoststag_demo`;
CREATE TABLE IF NOT EXISTS `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`google_id` VARCHAR(255) NOT NULL,
`name` VARCHAR(150) NOT NULL,
`email` VARCHAR(150) NOT NULL,
`picture` VARCHAR(255) DEFAULT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_google_id` (`google_id`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
هيكلة ملفات المشروع
يتكون النظام من 5 ملفات رئيسية متكاملة:
-
config.php: ملف الاتصال بقاعدة البيانات باستعمال MySQLi وضبط الإعدادات العامة. -
index.php: الصفحة الرئيسية التي تضم قسم الهيرو وزر تسجيل الدخول التفاعلي عبر جوجل. -
google_callback.php: المعالج الخلفي الذي يستقبل رمز JWT، يفحص توقيعه، ويتعامل مع قاعدة البيانات بالاستعلامات الجاهزة. -
profile.php: صفحة محمية تُظهر تفاصيل الحساب المسجل. -
logout.php: سكربت لتدمير الجلسة وتسجيل الخروج.
الأكواد البرمجية والتطبيق العملي
1. ملف الاتصال والإعدادات (config.php)
يُنشئ هذا الملف الاتصال بقاعدة البيانات باستخدام كائن mysqli ويضمن ضبط ترميز UTF-8 لتفادي مشاكل الحروف العربية:
<?php
// بدء الجلسة في حال لم تكن مفعلة
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// بيانات الاتصال بقاعدة البيانات
$db_host = 'localhost';
$db_user = 'root';
$db_pass = '';
$db_name = 'hoststag_demo';
// إجراء الاتصال باستخدام مكتبة MySQLi
$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
// التحقق من سلامة الاتصال
if (!$conn) {
die("خطأ في الاتصال بقاعدة البيانات: " . mysqli_connect_error());
}
// ضبط الترميز العربي الصحيح
mysqli_set_charset($conn, "utf8mb4");
// تعريف مفتاح Google Client ID ورابط الموقع
define('GOOGLE_CLIENT_ID', 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com');
define('SITE_URL', 'http://localhost/google-auth');
?>
تعريف الإعدادات والثوابت بداخل ملف config.php:
تم تعريف GOOGLE_CLIENT_ID و SITE_URL بداخل ملف الإعدادات والاتصال config.php باستخدام دالة define() لثلاثة أسباب رئيسية تتبع أفضل ممارسات البرمجة (Best Practices):
1️⃣ تطبيق مبدأ عدم التكرار (DRY - Don't Repeat Yourself): يُستخدم مفتاح Google Client ID في أكثر من ملف بداخل النظام:
-
في صفحة الواجهة (
index.php): لتلقين مكتبة جوجل الرسمية وسحب زر الدخول التفاعلي. -
في المعالج الخلفي (
google_callback.php): للتحقق الأمني من حقل الـaudبداخل رمز الـ JWT Token وتأكيد أن الرمز صادر لصالح تطبيقنا فقط.
بدلاً من كتابة المفتاح الطويل في الملفين بشكل منفصل، نقوم بتعريفه كـ ثابت (Constant) في ملف الإعدادات ليتوفر في جميع الملفات تلقائياً بمجرد استدعاء require_once 'config.php'.
2️⃣ سهولة التعديل وتغيير البيئة (Maintenance & Portability): عند تطوير أي مشروع، ننتقل عادة من بيئة التطبيق المحلي (localhost) إلى خادم الاستضافة الحقيقي (hoststag.com):
-
إذا كان المفتاح أو رابط الموقع مكتوباً يدويًا بداخل الصفحات، ستضطر للبحث والتعديل في كل ملف على حدة، وهو ما قد يتسبب في نسيان أحد الملفات أو حدوث خطأ كتابي (Typo).
-
بوجود الثوابت بداخل
config.php، يمكنك نقل الموقع إلى السيرفر الحي أو تغيير مفتاح جوجل بضغطات بسيطة عبر تعديل سطر واحد فقط بداخل ملف الإعدادات، ليتحدث السكربت بالكامل فوراً.
3️⃣ الفصل بين الإعدادات والواجهات (Separation of Concerns): من الأفضل دائماً فصل بيانات الاتصال، المفاتيح الأمنية، والروابط العامة عن ملفات العرض (HTML/UI) والملفات البرمجية التنفيذية. هذا يجعل هيكل المشروع منظماً، ويسهل على أي مطور آخر يقرأ الكود فهم مكان الإعدادات الرئيسية مباشرة.
2. الصفحة الرئيسية وزر الدخول (index.php)
تستخدم هذه الصفحة مكتبة جوجل الرسمية https://accounts.google.com/gsi/client لبناء زر ورود احترافي ومتجاوب:
<?php
require_once 'config.php';
// توجيه المستخدم تلقائياً لصفحة الملف الشخصي إذا كان قد سجل دخوله مسبقاً
if (isset($_SESSION['user_id'])) {
header("Location: profile.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تسجيل الدخول عبر Google - منصة HostStag</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css">
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap" rel="stylesheet">
<script src="https://accounts.google.com/gsi/client" async defer></script>
<style>
body { font-family: 'Cairo', sans-serif; background-color: #f8f9fa; }
.hero-section { background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%); color: white; padding: 70px 0 50px 0; border-bottom-left-radius: 35px; border-bottom-right-radius: 35px; }
.login-card { border: none; border-radius: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.08); margin-top: -40px; background: #ffffff; }
.btn-google-container { display: flex; justify-content: center; margin-top: 20px; }
</style>
</head>
<body>
<section class="hero-section text-center">
<div class="container">
<h1 class="fw-bold display-6 mb-2">مرحباً بك في منصة HostStag</h1>
<p class="lead text-white-50 mx-auto" style="max-width: 600px;">
انضم إلى عالم السكربتات البرمجية والحلول الذكية. سجّل دخولك بنقرة واحدة عبر حسابك في جوجل.
</p>
</div>
</section>
<main class="container my-5">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="card login-card p-4 p-md-5 text-center">
<h3 class="fw-bold mb-1">تسجيل الدخول</h3>
<p class="text-muted small mb-4">انقر على الزر أدناه للدخول فوراً</p>
<!-- إعدادات مكتبة Google GSI -->
<div id="g_id_onload"
data-client_id="<?php echo GOOGLE_CLIENT_ID; ?>"
data-login_uri="<?php echo SITE_URL; ?>/google_callback.php"
data-auto_prompt="false">
</div>
<!-- عرض زر تسجيل الدخول المعتمد -->
<div class="btn-google-container">
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="right"
data-width="320"
data-locale="ar">
</div>
</div>
<div class="mt-4 pt-3 border-top">
<small class="text-muted">تسجيلك يعني موافقتك على <a href="#" class="text-decoration-none">شروط الاستخدام</a> و <a href="#" class="text-decoration-none">سياسة الخصوصية</a>.</small>
</div>
</div>
</div>
</div>
</main>
</body>
</html>
3. معالج الدخول بالاستعلامات الجاهزة (google_callback.php)
هذا الملف هو المحرك الخلفي الرئيسي. عند إتمام عملية التحقق لدى جوجل، تُرْسِل السيرفرات رمزا مشفرا يسمى JSON Web Token (JWT).
يقوم هذا السكربت بـ:
-
فك تشفير حمولة الـ JWT دون الحاجة لمكتبات خارجية ثقيلة.
-
التحقق من أن الرمز صادق وصادر لصالح تطبيقنا (
aud === GOOGLE_CLIENT_ID). -
استخدام MySQLi Prepared Statements للبحث عن الحساب والتأكد هل هو موجود مسبقاً، ثم تحديث بياناته أو إنشاء حساب جديد بأمان مطلق:
<?php
require_once 'config.php';
// التأكد من وصول الرمز المشفر قادماً من Google
if (!isset($_POST['credential'])) {
header("Location: index.php");
exit();
}
$id_token = $_POST['credential'];
// تفكيك عناصر رمز JWT الثلاثة (Header . Payload . Signature)
$token_parts = explode('.', $id_token);
if (count($token_parts) !== 3) {
die("استجابة غير صالحة من سيرفرات Google.");
}
// فك تشفير جسم الرسالة (Payload)
$payload_json = base64_decode(strtr($token_parts[1], '-_', '+/'));
$data = json_decode($payload_json, true);
// التحقق من صحة مصدر البيانات ومطابقتها لـ Client ID الخاص بنا
if (!$data || !isset($data['sub']) || $data['aud'] !== GOOGLE_CLIENT_ID) {
die("فشل التوثيق الأمني: الرمز غير متوافق مع هذا الموقع.");
}
// استخراج بيانات المستحدم الأساسية
$google_id = $data['sub'];
$name = $data['name'];
$email = $data['email'];
$picture = isset($data['picture']) ? $data['picture'] : '';
// 1. البحث عن المستخدم في قاعدة البيانات باستخدام MySQLi Prepared Statement
$stmt = mysqli_prepare($conn, "SELECT id FROM users WHERE google_id = ? OR email = ?");
mysqli_stmt_bind_param($stmt, "ss", $google_id, $email);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
if ($user) {
// 2. إذا كان الحساب موجوداً: تحديث البيانات بالاستعلام الجاهز
$user_id = $user['id'];
$updateStmt = mysqli_prepare($conn, "UPDATE users SET name = ?, picture = ?, google_id = ? WHERE id = ?");
mysqli_stmt_bind_param($updateStmt, "sssi", $name, $picture, $google_id, $user_id);
mysqli_stmt_execute($updateStmt);
mysqli_stmt_close($updateStmt);
} else {
// 3. إذا كان مستخدماً جديداً: إضافة الحساب بالاستعلام الجاهز
$insertStmt = mysqli_prepare($conn, "INSERT INTO users (google_id, name, email, picture) VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($insertStmt, "ssss", $google_id, $name, $email, $picture);
mysqli_stmt_execute($insertStmt);
$user_id = mysqli_insert_id($conn);
mysqli_stmt_close($insertStmt);
}
// تخزين بيانات الجلسة (Session)
$_SESSION['user_id'] = $user_id;
$_SESSION['user_name'] = $name;
$_SESSION['user_email'] = $email;
$_SESSION['user_picture'] = $picture;
// التوجيه لصفحة الملف الشخصي
header("Location: profile.php");
exit();
?>
4. صفحة الملف الشخصي المحمية (profile.php)
صفحة بسيطة تضمن عدم إمكانية دخول الزوار غير المسجلين، وتستعرض تفاصيل البريد والصورة القادمة من جوجل:
<?php
require_once 'config.php';
// حماية الصفحة: إعادة التوجيه للصفحة الرئيسية في حال عدم وجود جلسة نشطة
if (!isset($_SESSION['user_id'])) {
header("Location: index.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>الملف الشخصي - منصة HostStag</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css">
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Cairo', sans-serif; background-color: #f4f6f9; }
.profile-card { border: none; border-radius: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.06); background: #ffffff; }
.profile-avatar { width: 100px; height: 100px; border-radius: 50%; border: 3px solid #0d6efd; object-fit: cover; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top shadow-sm">
<div class="container">
<a class="navbar-brand fw-bold" href="index.php"><span class="text-primary">Host</span>Stag</a>
<div class="d-flex align-items-center gap-3">
<span class="text-white small d-none d-md-inline">مرحباً، <?php echo htmlspecialchars($_SESSION['user_name']); ?></span>
<a href="logout.php" class="btn btn-outline-danger btn-sm px-3">تسجيل الخروج</a>
</div>
</div>
</nav>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card profile-card p-4 p-md-5 text-center">
<div class="mb-3">
<img src="<?php echo htmlspecialchars($_SESSION['user_picture']); ?>" alt="الصورة الشخصية" class="profile-avatar shadow-sm">
</div>
<h3 class="fw-bold text-dark mb-1"><?php echo htmlspecialchars($_SESSION['user_name']); ?></h3>
<p class="text-muted mb-4"><?php echo htmlspecialchars($_SESSION['user_email']); ?></p>
<div class="alert alert-success py-2 small" role="alert">
تم تفعيل الجلسة والتسجيل بنجاح عن طريق <strong>Google OAuth 2.0</strong>
</div>
<div class="row text-start mt-4 pt-3 border-top">
<div class="col-6 mb-2">
<small class="text-muted d-block">رقم المستخدم (ID)</small>
<span class="fw-bold">#<?php echo $_SESSION['user_id']; ?></span>
</div>
<div class="col-6 mb-2">
<small class="text-muted d-block">حالة التوثيق</small>
<span class="badge bg-success">حساب موثّق</span>
</div>
</div>
<div class="mt-4">
<a href="index.php" class="btn btn-primary px-4 fw-bold">العودة للرئيسية</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
5. سكربت إنهاء الجلسة (logout.php)
يقوم بتفريغ مصفوفة الجلسة وإلغاء الكوكيز وتديمر الجلسة بالكامل لإغلاق الحساب بأمان:
<?php
require_once 'config.php';
// تفريغ كافة متغيرات الجلسة
$_SESSION = array();
// إزالة كوكيز الجلسة من المتصفح
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// تدمير الجلسة
session_destroy();
// التوجيه للصفحة الرئيسية
header("Location: index.php");
exit();
?>
الإجراءات الأمنية والتوصيات التقنية
-
استخدام
mysqli_prepareوسلسلةbind_param: تضمن منع ثغرات SQL Injection بنسبة 100%، حيث تقوم دالةbind_paramبتحديد نوع كل متغير (sللسنلسة النصية وiللرقم الصحيح). -
بروتوكول HTTPS المشفر: تفرض شركة Google تشغيل النطاقات عبر بروتوكول
https://المشفر بجميع البيئات الحية لضمان عدم اعتراض الـ JWT Tokens أثناء الانتقال. -
تطهير المخرجات بـ
htmlspecialchars: عند عرض اسم المستخدم أو بريده في صفحات HTML، نقوم بتغليفه بالدالةhtmlspecialcharsلحماية الموقع من ثغرات Cross-Site Scripting (XSS).
💡 هل تريد توفير وقتك والحصول على النسخة البرمجية الكاملة الجاهزة؟
إذا كنت لا تملك الوقت الكافي لبناء السكربت يدوياً، أو ترغب في الحصول على حزمة برمجية منظمة وجاهزة للرفع المباشر على خادم استضافتك (cPanel / Namecheap) دون الحاجة للتعامل مع كتابة الأكواد بنفسك، فقد قمنا بإعداد السكربت المدفوع المكتمل من HostStag:
- ⚡ جاهز للرفع والتحديث المباشر: مجلد كامل منظم يحتوي على جميع الملفات مفحوصة ومنقحة برمجياً.
- 🛡️ أعلى معايير الحماية: مدمج بتقنيات MySQLi Prepared Statements وتأمين الجلسات لمنع ثغرات SQLi و XSS.
- 🖼️ دعم معالجة الصور: خيار مدمج لتخزين صور المستخدمين محلياً أو استخدام Google CDN.
- 📱 واجهات Bootstrap 5 متجاوبة 100%: تصميم عربي متناسق يدعم الهواتف والشاشات الذكية.
باستخدام طريقة MySQLi Prepared Statements ومكتبات Google Identity Services الحديثة، أصبحت تملك الآن نظام تسجيل دخول سريع، مبسط، عالي الأمان، وجاهز للتطبيق المباشر في موقعك.