login and registration by react and node js

Login and Registration by react and node js

لو بتدور على طريقة سهلة وآمنة عشان تعمل تسجيل دخول وتسجيل حساب جديد، فالحل هنا باستخدام 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 (
        <Router>
            <Routes>
                <Route path="/" element={<Navigate to="/login" />} />
                <Route path="/register" element={<Register />} />
                <Route path="/login" element={<Login />} />
                <Route
                    path="/welcome"
                    element={
                        <PrivateRoute>
                            <Welcome />
                        </PrivateRoute>
                    }
                />
                <Route path="*" element={<div>404 Not Found</div>} />
            </Routes>
        </Router>
    );
}

export default App;
				
			

المكتبات والمكونات المستخدمة:

  • 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 (
        <form onSubmit={handleSubmit}>
            <h1>Register</h1>
            <input name="username" placeholder="Username" onChange={handleChange} />
            <input name="email" placeholder="Email" onChange={handleChange} />
            <input name="password" type="password" placeholder="Password" onChange={handleChange} />
            <button type="submit">Register</button>
            <p>
                Already have an account? <Link to="/login">Login here</Link>
            </p>
        </form>
    );
};

export default Register;

				
			

المكتبات المستخدمة:

  • useState:
    Hook في React بيستخدم لإدارة الحالة (State) داخل المكون. في الحالة دي، بنستخدمه لتخزين بيانات النموذج (Form).

  • useNavigate:
    Hook من مكتبة react-router-dom بيُستخدم لإعادة توجيه المستخدم إلى صفحة مختلفة.

  • axios:
    مكتبة HTTP لتسهيل التعامل مع الطلبات (Requests) إلى السيرفر.

  • Link:
    مكون من react-router-dom لإنشاء روابط تنقل بين الصفحات بدون إعادة تحميل الصفحة.

النقاط المهمة في الواجهة:

  1. <form onSubmit={handleSubmit}>:
    عند الضغط على زر التسجيل، يتم استدعاء handleSubmit.

  2. حقول الإدخال (Inputs):

    • name="username": تحديد اسم الحقل، وده بيُستخدم في handleChange لتحديث البيانات المناسبة.
    • onChange={handleChange}: استدعاء وظيفة handleChange عند تغيير أي قيمة في الحقل.
  3. زر التسجيل (Register):
    عند الضغط عليه، يتم إرسال النموذج.

  4. الرابط لتسجيل الدخول:
    لو المستخدم عنده حساب بالفعل، بيقدر يضغط على الرابط 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 (
        <form onSubmit={handleSubmit}>
            <h1>Login</h1>
            <input name="email" placeholder="Email" onChange={handleChange} />
            <input name="password" type="password" placeholder="Password" onChange={handleChange} />
            <button type="submit">Login</button>
            <p>
                Don't have an account? <Link to="/register">Register here</Link>
            </p>
        </form>
    );
};

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:

  1. e.preventDefault():
    منع التحديث الافتراضي للصفحة عند الإرسال.

  2. إرسال البيانات إلى السيرفر:
    باستخدام axios.post يتم إرسال بيانات النموذج (formData) إلى السيرفر عند /login.

  3. رد السيرفر:

    • لو تسجيل الدخول ناجح:

      • السيرفر يرجع Token (JWT) واسم المستخدم.
      • تظهر رسالة “Login successful!” باستخدام alert.
      • يتم تخزين التوكن واسم المستخدم في localStorage
    • يتم التحقق من تخزين التوكن بنجاح:
      • لو موجود، يتم إعادة التوجيه إلى صفحة الترحيب باستخدام navigate('/welcome').
      • لو مش موجود، تظهر رسالة خطأ.
  • لو فشل تسجيل الدخول:

    • يتم عرض رسالة الخطأ المرسلة من السيرفر (أو رسالة افتراضية “Login failed”).

5. واجهة المستخدم (UI):

النقاط المهمة في الواجهة:

  1. <form onSubmit={handleSubmit}>:
    عند الضغط على زر تسجيل الدخول، يتم استدعاء الوظيفة handleSubmit لإرسال البيانات.

  2. حقول الإدخال (Inputs):

    • name="email" و name="password":
      أسماء الحقول لتحديد القيم التي يتم تحديثها.
    • onChange={handleChange}:
      يتم استدعاء وظيفة handleChange عند تغيير أي قيمة في الحقول.
  3. زر تسجيل الدخول (Login):
    عند الضغط عليه يتم إرسال النموذج.

  4. الرابط إلى صفحة التسجيل:
    يوفر رابط لنقل المستخدم إلى صفحة 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 : <Navigate to="/login" />;
};

export default PrivateRoute;
				
			

PrivateRoute:

  • المدخلات:

    • children:
      مكون أو عناصر (Components) يتم تمريرها كأطفال داخل المسار المحمي.
  • العملية:

    1. التحقق من وجود التوكن في 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 (
        <div>
            <h1>Welcome to the App!</h1>
            <p>You have successfully logged in.</p>
            <button class="logout" onClick={handleLogout}>Logout</button>
        </div>
    );
};

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
				
			
Login and Registration by react and node js
Result

Login and Registration by react and node js

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top