لو بتدور على طريقة سهلة وآمنة عشان تعمل تسجيل دخول وتسجيل حساب جديد، فالحل هنا باستخدام React وNode.js. التطبيق ده معمول عشان يوفرلك تجربة مريحة وسريعة، سواء كنت عايز تسجل دخولك أو تبدأ بحساب جديد.
واجهة المستخدم معمولة بـ React بتصميم عصري وسهل، عشان تقدر تتعامل معاه بكل سهولة على أي جهاز. أما في الخلفية، Node.js هي اللي بتدير البيانات بسرعة وكفاءة، وبتتعامل مع MySQL عشان تحفظ بياناتك بأمان.
كل اللي عليك إنك تدخل اسم المستخدم والباسورد، ولو جديد تقدر تسجل بكل سهولة. وكمان بمجرد تسجيل الدخول، هيظهر اسمك في صفحة “مرحبًا”، وده بيخليك تحس إنك جزء من التجربة.
لو عايز تسهّل على نفسك تسجيل الدخول، وتستفيد بتجربة آمنة ومتطورة، يبقى الحل معانا. البرنامج ده معمول ليك عشان يوفر كل اللي محتاجه بأسلوب بسيط ومريح. متفوتش الفرصة.
المتطلبات
يمكنك الرجوع للمقال السابقة لمعرفة البرامج المطلوبة في حالة التطبيق علي تنفيذ مشروع باستخدام رياكت ونود جي اس
هيكل المشروع
إعداد خادم Node.js
قم بانشاء مجلد للمشروع وبداخلة مجلد اخر للسيرفر (backend) مثال ذلك
mkdir server
cd server
وبداخل مجلد server قم بإنشاء مشروع Node.js وتثبيت المكتبات اللازمة
npm init -y
npm install express mysql bcrypt jsonwebtoken cors body-parser
npm init -y
:
ده بيعمل ملفpackage.json
في المشروع بتاعك بشكل سريع ومن غير ما يسألك أي أسئلة (عشان الـ-y
). الملف ده بيحتوي على معلومات عن المشروع زي اسم المشروع، الإصدار، الـ dependencies، وهكذا.npm install express mysql bcrypt jsonwebtoken cors body-parser
:
ده بينزل مجموعة من المكتبات (dependencies) اللي غالبًا هتستخدمها في المشروع بتاعك. وده شرح كل واحدة:express
:
إطار عمل لتطوير تطبيقات الويب والسيرفرات باستخدام Node.js. بيخلّيك تعمل API بسهولة.mysql
:
مكتبة للتعامل مع قاعدة بيانات MySQL من خلال الكود في Node.js.bcrypt
:
مكتبة لتشفير الباسوردات وحمايتها. غالبًا بتستخدمها لما تيجي تسجل أو تتحقق من المستخدمين.jsonwebtoken
:
مكتبة للتعامل مع الـ JWT (JSON Web Tokens) اللي بتستخدمها لتأمين الـ APIs والتحقق من المستخدمين.cors
:
مكتبة بتسمح للـ API بتاعتك إنها تتعامل مع طلبات من دومينات مختلفة. مهمة جدًا لما يكون السيرفر بتاعك على دومين مختلف عن الفرونت إند.body-parser
:
مكتبة بتستخدمها عشان تعرف تقرأ البيانات اللي جاية في طلبات الـ POST والـ PUT، زي البيانات اللي بتتبعت في الفورم.
إعداد قاعدة البيانات (MySQL)
قم بإنشاء قاعدة بيانات تحتوي على جدول المستخدمينمن داخل برنامج phpmyadmin
CREATE DATABASE auth_app;
USE auth_app;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE
);
قم بانشاء ملف server.js داخل مجلد server وقم بنسخ الكود التالي
const express = require('express');
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 5000;
app.use(cors());
app.use(bodyParser.json());
// Database Connection
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'auth_app'
});
db.connect(err => {
if (err) {
console.error('Database connection failed:', err);
} else {
console.log('Connected to database');
}
});
// Routes
// Register
app.post('/register', async (req, res) => {
const { username, password, email } = req.body;
if (!username || !password || !email) {
return res.status(400).send('All fields are required');
}
const hashedPassword = await bcrypt.hash(password, 10);
const sql = 'INSERT INTO users (username, password, email) VALUES (?, ?, ?)';
db.query(sql, [username, hashedPassword, email], (err, result) => {
if (err) {
console.error(err);
res.status(500).send('User registration failed');
} else {
res.status(201).send('User registered successfully');
}
});
});
// Login
app.post('/login', (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).send('All fields are required');
}
const sql = 'SELECT * FROM users WHERE email = ?';
db.query(sql, [email], async (err, results) => {
if (err) {
return res.status(500).send('Login failed');
}
if (results.length === 0) {
return res.status(401).send('User not found');
}
const user = results[0];
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).send('Invalid credentials');
}
const token = jwt.sign({ id: user.id }, 'secret_key', { expiresIn: '1h' });
res.status(200).json({ token });
});
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
1. إعدادات السيرفر:
const express = require('express');
استدعاء مكتبةExpress
لإنشاء السيرفر.const app = express();
إنشاء تطبيق Express.const port = 5000;
تحديد البورت اللي السيرفر هيشتغل عليه (في الحالة دي، البورت 5000).
2. إعدادات الميدل وير:
app.use(cors());
السماح بالتواصل بين السيرفر وأي دومين خارجي (Cross-Origin Requests).app.use(bodyParser.json());
السماح للسيرفر بفهم بيانات الـ JSON اللي بتتبعت في الطلبات.
3.إنشاء اتصال بقاعدة بيانات MySQL:
host
: اسم السيرفر (هنا قاعدة البيانات على نفس الجهاز، فا استخدمناlocalhost
).user
: اسم المستخدم لقاعدة البيانات (هناroot
، المستخدم الافتراضي).password
: الباسورد (هنا فاضي لأن الافتراضي بيبقى من غير باسورد).database
: اسم قاعدة البيانات اللي هتتعامل معاها (auth_app
).
4. Routes (المسارات):
تسجيل المستخدم:
استقبال البيانات:
بيستقبل البيانات (username
,password
,email
) من الطلب.التحقق من البيانات:
بيتأكد إن كل الحقول مليانة. لو في حقل ناقص، بيرجع رد بخطأ (400 Bad Request
).تشفير الباسورد:
بيستخدم مكتبةbcrypt
لتشفير الباسورد (الـ10
دي عدد الـ salt rounds).إضافة المستخدم لقاعدة البيانات:
باستخدام SQL Query، بيضيف البيانات لجدولusers
. لو حصل خطأ في الإضافة، بيرجع رد بخطأ (500 Internal Server Error
).
تسجيل الدخول:
استقبال البيانات:
بيستقبل البريد الإلكتروني (email
) والباسورد (password
).التحقق من البيانات:
لو أي حقل ناقص، بيرجع رد بخطأ (400 Bad Request
).التحقق من المستخدم:
- بيبحث عن المستخدم في قاعدة البيانات باستخدام البريد الإلكتروني.
- لو المستخدم مش موجود، بيرجع رد بخطأ (
401 Unauthorized
).
التأكد من صحة الباسورد:
بيستخدمbcrypt.compare
عشان يقارن الباسورد اللي جاية من المستخدم مع الباسورد المشفرة في قاعدة البيانات.توليد التوكن (JWT):
لو الباسورد صحيح، بيولّد JSON Web Token (JWT
) باستخدام مكتبةjsonwebtoken
. التوكن ده بيكون:- يحتوي على معرّف المستخدم (
id
). - له مدة صلاحية (هنا ساعة واحدة).
- يحتوي على معرّف المستخدم (
5. تشغيل السيرفر:
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
- تشغيل السيرفر على البورت 5000.
- كل حاجة جاهزة لاستقبال الطلبات
إعداد واجهة React
من داخل المشروع نقوم بانشاء الواجهة الامامية لرياكت
npx create-react-app client
cd client
npm install axios react-router-dom
ملف App.js
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import Register from './Register';
import Login from './Login';
import Welcome from './Welcome';
import PrivateRoute from './PrivateRoute'; // استيراد مكون PrivateRoute
import './App.css';
function App() {
return (
} />
} />
} />
}
/>
404 Not Found
المكتبات والمكونات المستخدمة:
import { BrowserRouter as Router, Route, Routes, Navigate }
:
استيراد عناصر إدارة التنقل من مكتبةreact-router-dom
.Router
: يحدد أن التطبيق يستخدم الـ Routing.Routes
: يحتوي على كل مسارات التطبيق.Route
: يحدد مسار معين وصفحة (أو مكون) مرتبطة بيه.Navigate
: بيعمل إعادة توجيه (Redirect) من مسار معين إلى مسار آخر.
Register
,Login
,Welcome
:
دي المكونات اللي بتمثل الصفحات:Register
: صفحة تسجيل مستخدم جديد.Login
: صفحة تسجيل الدخول.Welcome
: صفحة الترحيب (محمية).
PrivateRoute
:
مكون مخصص بيحمي صفحة معينة بحيث يسمح بالوصول ليها بس لو المستخدم مسجل دخوله.App.css
:
ملف CSS اللي بيحدد تصميم التطبيق.
باختصار
لما المستخدم يفتح /
:
يتم توجيهه تلقائيًا إلى /login
.
لما يفتح /register
:
يقدر يسجل كمستخدم جديد.
لما يسجل دخول وينجح (مع وجود Token):
يقدر يدخل على /welcome
.
لو حاول يدخل على /welcome
من غير تسجيل دخول:
يتم توجيهه إلى /login
.
أي مسار غلط:
تظهر صفحة “404 Not Found”.
ملف Register.js
import React, { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import axios from 'axios';
const Register = () => {
const [formData, setFormData] = useState({ username: '', email: '', password: '' });
const navigate = useNavigate(); // لاستخدام إعادة التوجيه
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:5000/register', formData);
alert(response.data);
// إعادة التوجيه إلى صفحة Welcome
navigate('/welcome');
} catch (error) {
alert(error.response.data || 'Registration failed');
}
};
return (
);
};
export default Register;
المكتبات المستخدمة:
useState
:
Hook في React بيستخدم لإدارة الحالة (State) داخل المكون. في الحالة دي، بنستخدمه لتخزين بيانات النموذج (Form).useNavigate
:
Hook من مكتبةreact-router-dom
بيُستخدم لإعادة توجيه المستخدم إلى صفحة مختلفة.axios
:
مكتبة HTTP لتسهيل التعامل مع الطلبات (Requests) إلى السيرفر.Link
:
مكون منreact-router-dom
لإنشاء روابط تنقل بين الصفحات بدون إعادة تحميل الصفحة.
النقاط المهمة في الواجهة:
<form onSubmit={handleSubmit}>
:
عند الضغط على زر التسجيل، يتم استدعاءhandleSubmit
.حقول الإدخال (Inputs):
name="username"
: تحديد اسم الحقل، وده بيُستخدم فيhandleChange
لتحديث البيانات المناسبة.onChange={handleChange}
: استدعاء وظيفةhandleChange
عند تغيير أي قيمة في الحقل.
زر التسجيل (Register):
عند الضغط عليه، يتم إرسال النموذج.الرابط لتسجيل الدخول:
لو المستخدم عنده حساب بالفعل، بيقدر يضغط على الرابطLogin here
عشان ينتقل لصفحة تسجيل الدخول.
النقاط الأساسية في الكود:
- النموذج ديناميكي، أي تغييرات في الحقول يتم تحديثها في
formData
. - يتم إرسال البيانات للسيرفر عند الإرسال، مع عرض رد السيرفر للمستخدم.
- إذا كان التسجيل ناجحًا، يتم إعادة التوجيه إلى صفحة
/welcome
. - واجهة المستخدم بسيطة وسهلة الفهم، مع روابط إضافية لصفحات أخرى.
ملف Login.js
import React, { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import axios from 'axios';
const Login = () => {
const [formData, setFormData] = useState({ email: '', password: '' });
const navigate = useNavigate(); // لاستخدام إعادة التوجيه
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:5000/login', formData);
const { token, username } = response.data.token;
alert('Login successful!');
// تخزين التوكن في localStorage
localStorage.setItem('token', token);
localStorage.setItem('username', username); // تخزين اسم المستخدم
// التحقق من وجود التوكن قبل التوجيه
const storedToken = localStorage.getItem('token', 'username');
if (storedToken) {
navigate('/welcome');
} else {
alert('Token not stored correctly');
}
} catch (error) {
alert(error.response?.data || 'Login failed');
}
};
return (
);
};
export default Login;
1. المكتبات المستخدمة:
useState
:
Hook في React يُستخدم لإدارة الحالة (State) داخل المكون.useNavigate
:
Hook من مكتبةreact-router-dom
لإعادة توجيه المستخدم بين الصفحات.axios
:
مكتبة لإرسال الطلبات HTTP إلى السيرفر.Link
:
مكون منreact-router-dom
يُستخدم لإنشاء روابط تنقل بين الصفحات بدون تحديث الصفحة.
2. الحالة (State):
formData
:
تحتوي على بيانات النموذج المدخلة من المستخدم: البريد الإلكتروني وكلمة المرور.setFormData
:
وظيفة تُستخدم لتحديث الحالة بناءً على مدخلات المستخدم.الحالة الابتدائية:
القيم فارغة (email: '', password: ''
).
3. التعامل مع المدخلات (Input Handling):
handleChange
:
تُحدث بياناتformData
بناءً على الحقل الذي قام المستخدم بتغييره.طريقة العمل:
e.target.name
: يحدد اسم الحقل (email
أوpassword
).e.target.value
: يأخذ القيمة التي أدخلها المستخدم في الحقل.- يُحدث البيانات باستخدام
setFormData
.
4. إرسال البيانات (Submit Handling):
الوظيفة handleSubmit
:
e.preventDefault()
:
منع التحديث الافتراضي للصفحة عند الإرسال.إرسال البيانات إلى السيرفر:
باستخدامaxios.post
يتم إرسال بيانات النموذج (formData
) إلى السيرفر عند/login
.رد السيرفر:
لو تسجيل الدخول ناجح:
- السيرفر يرجع Token (JWT) واسم المستخدم.
- تظهر رسالة “Login successful!” باستخدام
alert
. - يتم تخزين التوكن واسم المستخدم في localStorage
- يتم التحقق من تخزين التوكن بنجاح:
- لو موجود، يتم إعادة التوجيه إلى صفحة الترحيب باستخدام
navigate('/welcome')
. - لو مش موجود، تظهر رسالة خطأ.
- لو موجود، يتم إعادة التوجيه إلى صفحة الترحيب باستخدام
- يتم التحقق من تخزين التوكن بنجاح:
لو فشل تسجيل الدخول:
- يتم عرض رسالة الخطأ المرسلة من السيرفر (أو رسالة افتراضية “Login failed”).
5. واجهة المستخدم (UI):
النقاط المهمة في الواجهة:
<form onSubmit={handleSubmit}>
:
عند الضغط على زر تسجيل الدخول، يتم استدعاء الوظيفةhandleSubmit
لإرسال البيانات.حقول الإدخال (Inputs):
name="email"
وname="password"
:
أسماء الحقول لتحديد القيم التي يتم تحديثها.onChange={handleChange}
:
يتم استدعاء وظيفةhandleChange
عند تغيير أي قيمة في الحقول.
زر تسجيل الدخول (Login):
عند الضغط عليه يتم إرسال النموذج.الرابط إلى صفحة التسجيل:
يوفر رابط لنقل المستخدم إلى صفحة Register باستخدام مكون<Link>
.
6. الهدف من الكود:
- تمكين المستخدم من تسجيل الدخول باستخدام بياناته (البريد وكلمة المرور).
- حفظ التوكن واسم المستخدم في localStorage لتسهيل التحقق من الجلسة.
- إعادة توجيه المستخدم إلى صفحة الترحيب بعد تسجيل الدخول بنجاح.
ملف PrivateRoute.js
import React from 'react';
import { Navigate } from 'react-router-dom';
const PrivateRoute = ({ children }) => {
const isAuthenticated = localStorage.getItem('token'); // التحقق من وجود التوكن
return isAuthenticated ? children : ;
};
export default PrivateRoute;
PrivateRoute
:
المدخلات:
children
:
مكون أو عناصر (Components) يتم تمريرها كأطفال داخل المسار المحمي.
العملية:
التحقق من وجود التوكن في
localStorage
- إذا كان التوكن موجودًا، يعني أن المستخدم مُسجَّل الدخول.
- إذا لم يكن موجودًا، يعني أن المستخدم غير مُسجَّل الدخول.
الشرط:
- إذا كان
isAuthenticated
يحتوي على قيمة، يتم عرضchildren
(أي الصفحة أو المحتوى المحمي). - إذا لم يكن يحتوي على قيمة، يتم إعادة التوجيه تلقائيًا إلى مسار تسجيل الدخول (
/login
) باستخدام مكون<Navigate />
.
- إذا كان
ملف Welcome.js
import React from 'react';
const Welcome = () => {
const handleLogout = () => {
// هنا يمكنك إضافة الكود الخاص بتسجيل الخروج
// مثلاً: مسح التوكن من localStorage أو إعادة توجيه المستخدم إلى صفحة تسجيل الدخول
localStorage.removeItem('token'); // مثال: مسح التوكن من localStorage
window.location.href = '/login'; // إعادة توجيه المستخدم إلى صفحة تسجيل الدخول
};
return (
Welcome to the App!
You have successfully logged in.
);
};
export default Welcome;
تجربة المستخدم (UX):
قبل تسجيل الخروج:
- يظهر للمستخدم رسالة ترحيبية تؤكد أنه قد سجل دخوله بنجاح.
- يوجد زر واضح لتسجيل الخروج.
عند الضغط على زر تسجيل الخروج:
- يتم حذف التوكن من التخزين المحلي.
- يتم إعادة توجيه المستخدم مباشرةً إلى صفحة تسجيل الدخول.
ملف App.css
ملف css لتنسيق العرض لجميع الصفحات
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background: #f9f9f9;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
form {
background: #ffffff;
padding: 20px 30px;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
width: 300px;
}
form input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
form input:focus {
border-color: #007bff;
outline: none;
}
form button,.logout {
width: 100%;
padding: 10px;
background-color: #007bff;
color: #ffffff;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-top: 10px;
}
form button:hover,.logout:hover {
background-color: #0056b3;
}
.logout {
width: 100%;
padding: 10px;
background-color: #c20000;
color: #ffffff;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-top: 10px;
}
.logout:hover {
background-color: #800202;
}
h1 {
text-align: center;
font-size: 24px;
color: #333;
}
.alert {
color: red;
font-size: 14px;
margin-bottom: 10px;
}
p {
margin-top: 15px;
font-size: 14px;
color: #555;
text-align: center;
}
p a {
color: #007bff;
text-decoration: none;
font-weight: bold;
}
p a:hover {
text-decoration: underline;
}
تشغيل المشروع
من داخل المشروع الجزر نقوم باستخدام الكود التالي لدمج backend مع frontend في كود واحد
npm install concurrently
بعد ذلك نقوم بتعديل الكود داخل ملف package.json الخاص بالباك اند بحيث تكون scripts بنفس الشكل التالي
"scripts": {
"server": "node server.js",
"client": "npm start --prefix client",
"start": "concurrently \"npm run server\" \"npm run client\""
},
وفي النهاية امر التشغيل التالي
npm start