كيفية بناء نظام تسجيل مستخدمين باستخدام Django REST و Next.js: API + Auth + Frontend

كيفية بناء نظام تسجيل مستخدمين باستخدام Django REST و Next.js: API + Auth + Frontend

في هذا الدليل العملي سنتعرف خطوة بخطوة على كيفية بناء نظام تسجيل مستخدمين وتسجيل دخول متكامل باستخدام Django REST Framework في الخلفية (Backend) و Next.js في الواجهة (Frontend)، مع التركيز على:

  • إنشاء API للمصادقة والتعامل مع المستخدمين في Django REST.
  • إدارة الجلسات (Sessions) أو التوكنات (Tokens / JWT) بشكل آمن.
  • حماية الصفحات في Next.js ومنع الوصول غير المصرح به.
  • الهيكلية العامة لمشروع Django REST Next.js Authentication.

إذا كنت مهتمًا بالمصادقة المتقدمة مثل OAuth2 أو أنظمة تسجيل الدخول بدون كلمة مرور، يمكنك لاحقًا الاطلاع على: تنفيذ OAuth2 في Django و FastAPI و بناء نظام تسجيل دخول بدون كلمات مرور باستخدام Magic Links.

1. نظرة عامة على المعمارية: Django REST + Next.js

نحن نبني نظامًا من جزئين:

  1. Backend: مشروع Django مع Django REST Framework يوفر API endpoints:
    • تسجيل حساب جديد (Register).
    • تسجيل الدخول (Login) وإصدار توكن أو جلسة.
    • جلب بيانات المستخدم الحالي (Profile / Me).
    • تسجيل الخروج (Logout).
  2. Frontend: مشروع Next.js يتعامل مع هذه الـ APIs:
    • صفحات تسجيل الدخول والتسجيل.
    • حفظ حالة المستخدم (logged in / logged out).
    • حماية صفحات معينة من الوصول بدون تسجيل دخول.

هناك خياران رئيسيان للمصادقة في هذه التوليفة:

  • Session-based Auth: Django يدير الجلسات باستخدام Cookies (مناسب عندما يكون الـ Frontend و Backend على نفس الدومين أو دومينات فرعية).
  • Token / JWT-based Auth: Django REST يصدر توكن (مثل JWT) يحفظه الـ Frontend ويُرسل مع كل طلب.

في هذا الشرح سنركز على JWT لأنه شائع جدًا مع واجهات SPA و Next.js، وسهل الدمج مع حماية الصفحات.

2. تجهيز مشروع Django REST

2.1 إنشاء مشروع وتثبيت الحزم

ابدأ بإنشاء بيئة عمل وتثبيت الحزم الأساسية:

pip install django djangorestframework djangorestframework-simplejwt

أنشئ مشروع وتطبيق Django:

django-admin startproject backend
cd backend
python manage.py startapp accounts

2.2 إعداد INSTALLED_APPS و REST_FRAMEWORK

في backend/settings.py أضف:

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework.authtoken',
    'accounts',
    'django.contrib.auth',
    'django.contrib.contenttypes',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

2.3 تهيئة Simple JWT

لاستخدام JWT من حزمة Simple JWT يمكنك إضافة بعض الإعدادات الاختيارية:

from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
    'AUTH_HEADER_TYPES': ('Bearer',),
}

2.4 نموذج المستخدم (User Model)

يمكنك استخدام النموذج الافتراضي django.contrib.auth.models.User أو إنشاء نموذج مخصص. للبساطة سنستخدم النموذج الافتراضي.

2.5 إنشاء Serializers للمستخدم والمصادقة

في accounts/serializers.py:

from django.contrib.auth.models import User
from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email']

class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True,
                                     validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password', 'password2']

    def validate(self, attrs):
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError({'password': 'كلمتا المرور غير متطابقتين.'})
        return attrs

    def create(self, validated_data):
        validated_data.pop('password2')
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data.get('email', ''),
            password=validated_data['password']
        )
        return user

2.6 إنشاء Views للمصادقة

في accounts/views.py:

from rest_framework import generics, permissions
from rest_framework.response import Response
from django.contrib.auth.models import User
from .serializers import UserSerializer, RegisterSerializer
from rest_framework.views import APIView

class RegisterView(generics.CreateAPIView):
    queryset = User.objects.all()
    permission_classes = (permissions.AllowAny,)
    serializer_class = RegisterSerializer

class UserView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request):
        serializer = UserSerializer(request.user)
        return Response(serializer.data)

للتعامل مع JWT Tokens سنستخدم الviews الجاهزة من Simple JWT بدون الكثير من الكود.

2.7 إعداد URLs

في backend/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/accounts/', include('accounts.urls')),
]

ثم أنشئ accounts/urls.py:

from django.urls import path
from .views import RegisterView, UserView
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('register/', RegisterView.as_view(), name='register'),
    path('me/', UserView.as_view(), name='user-me'),
    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

بهذا أصبحت لديك API أساسية لـ Django REST Next.js Authentication تتضمن:

  • POST /api/accounts/register/: إنشاء مستخدم جديد.
  • POST /api/accounts/token/: تسجيل الدخول والحصول على access & refresh tokens.
  • POST /api/accounts/token/refresh/: تجديد التوكن.
  • GET /api/accounts/me/: جلب بيانات المستخدم الحالي.

3. إعداد CORS والأمان للتكامل مع Next.js

لكي يتمكن تطبيق Next.js من استهلاك الـ API، يجب السماح بالطلبات من أصل مختلف (Cross-Origin). استخدم حزمة django-cors-headers:

pip install django-cors-headers

في settings.py:

INSTALLED_APPS += [
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    # ...
]

CORS_ALLOWED_ORIGINS = [
    'http://localhost:3000',  # حيث يعمل Next.js
]

إذا احتجت إلى فهم أعمق للـ middleware في Django وكيفية تأثيرها على الطلبات والاستجابات، راجع: شرح طريقة استخدام Middleware في Django مع أمثلة وأفضل الممارسات.

4. بناء واجهة Next.js: تسجيل الدخول والتسجيل وحماية الصفحات

4.1 إنشاء مشروع Next.js

باستخدام TypeScript اختيارياً:

npx create-next-app@latest frontend
cd frontend

يمكن استخدام fetch أو axios، في المثال سنستخدم fetch المدمج.

4.2 هيكلية بسيطة لإدارة Auth في Next.js

سنحتاج إلى:

  • صفحة register لإرسال بيانات المستخدم إلى /api/accounts/register/.
  • صفحة login لإرسال بيانات الدخول إلى /api/accounts/token/.
  • تخزين التوكنات (يفضل HTTP-only Cookies لتقليل مخاطر XSS).
  • حماية الصفحات على مستوى الخادم باستخدام getServerSideProps أو middleware.

بسبب تعقيد إدارة الـ HTTP-only Cookies، سنعرض الفكرة العامة: Next.js يستقبل التوكنات من Django ويخزنها في Cookie عبر API Routes داخل Next.js، وليس مباشرة في localStorage من الـ Client.

4.3 صفحة التسجيل في Next.js

افترض أنك تستخدم هيكل الصفحات التقليدي (Pages Router):

في pages/register.tsx:

import { useState } from 'react';
import { useRouter } from 'next/router';

export default function RegisterPage() {
  const router = useRouter();
  const [form, setForm] = useState({ username: '', email: '', password: '', password2: '' });
  const [error, setError] = useState('');

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');

    const res = await fetch('http://localhost:8000/api/accounts/register/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form),
    });

    if (res.ok) {
      router.push('/login');
    } else {
      const data = await res.json();
      setError(data.detail || 'حدث خطأ أثناء التسجيل');
    }
  };

  return (
    <div>
      <h1>تسجيل حساب جديد</h1>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <form onSubmit={handleSubmit}>
        <input name="username" placeholder="اسم المستخدم" onChange={handleChange} />
        <input name="email" placeholder="البريد" onChange={handleChange} />
        <input type="password" name="password" placeholder="كلمة المرور" onChange={handleChange} />
        <input type="password" name="password2" placeholder="تأكيد كلمة المرور" onChange={handleChange} />
        <button type="submit">تسجيل</button>
      </form>
    </div>
  );
}

4.4 صفحة تسجيل الدخول وإدارة التوكنات

في مستوى الإنتاج يفضل استخدام API Routes في Next.js لتخزين الـ tokens في HTTP-only Cookies. لكن لفهم الفكرة بسرعة، سنوضح نموذجًا بسيطًا مع localStorage (مع أنه أقل أمانًا، لكنه شائع في المشاريع التجريبية).

في pages/login.tsx:

import { useState } from 'react';
import { useRouter } from 'next/router';

export default function LoginPage() {
  const router = useRouter();
  const [form, setForm] = useState({ username: '', password: '' });
  const [error, setError] = useState('');

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');

    const res = await fetch('http://localhost:8000/api/accounts/token/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form),
    });

    const data = await res.json();
    if (res.ok) {
      if (typeof window !== 'undefined') {
        localStorage.setItem('access', data.access);
        localStorage.setItem('refresh', data.refresh);
      }
      router.push('/dashboard');
    } else {
      setError(data.detail || 'بيانات الدخول غير صحيحة');
    }
  };

  return (
    <div>
      <h1>تسجيل الدخول</h1>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <form onSubmit={handleSubmit}>
        <input name="username" placeholder="اسم المستخدم" onChange={handleChange} />
        <input type="password" name="password" placeholder="كلمة المرور" onChange={handleChange} />
        <button type="submit">دخول</button>
      </form>
    </div>
  );
}

5. استهلاك API المحمية في Next.js

5.1 صفحة محمية (Dashboard)

ستحتاج إلى إرسال الـ access token في ترويسة Authorization إلى /api/accounts/me/ لجلب معلومات المستخدم.

في pages/dashboard.tsx:

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

interface User {
  id: number;
  username: string;
  email: string;
}

export default function DashboardPage() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    const access = typeof window !== 'undefined' ? localStorage.getItem('access') : null;
    if (!access) {
      router.push('/login');
      return;
    }

    const fetchUser = async () => {
      const res = await fetch('http://localhost:8000/api/accounts/me/', {
        headers: { Authorization: `Bearer ${access}` },
      });

      if (res.ok) {
        const data = await res.json();
        setUser(data);
      } else {
        // يمكن هنا محاولة استخدام refresh token أو إعادة التوجيه
        router.push('/login');
      }
      setLoading(false);
    };

    fetchUser();
  }, [router]);

  if (loading) return <p>جاري التحميل...</p>;

  if (!user) return null;

  return (
    <div>
      <h1>لوحة التحكم</h1>
      <p>مرحبًا، {user.username}</p>
      <p>بريدك: {user.email}</p>
    </div>
  );
}

5.2 حماية الصفحات على مستوى الخادم

الحماية على مستوى الخادم في Next.js (خصوصًا في وضع SSR) أكثر أمانًا، لأنها تمنع تحميل الصفحة من الأساس إذا لم يكن المستخدم مصدقًا. يمكن استخدام getServerSideProps لقراءة الـ token من Cookie والتحقق منه عبر Django.

الفكرة:

  1. عند تسجيل الدخول، يخزن Next.js الـ access/refresh tokens في Cookie (HTTP-only) عبر API Route.
  2. في getServerSideProps تقرأ هذه الـ Cookies وترسلها إلى Django للتحقق.
  3. إذا كان التوكن غير صالح، يتم إعادة التوجيه إلى صفحة تسجيل الدخول.

ذلك يتطلب طبقة إضافية في Next.js (API Routes) لتغليف طلبات المصادقة، لكنه يوفر أمانًا أعلى ويفصل الـ tokens عن JavaScript في المتصفح قدر الإمكان.

6. إدارة الجلسات وتجديد التوكن (Token Refresh)

في إعدادنا مع Django REST Simple JWT:

  • الـ access token قصير العمر (مثل 30 دقيقة).
  • الـ refresh token أطول عمرًا (مثل 7 أيام).

عندما ينتهي access token، يمكن للـ Frontend إرسال:

POST /api/accounts/token/refresh/
{
  "refresh": "<refresh_token_here>"
}

ويستقبل access token جديد. في Next.js يمكنك إنشاء دالة مساعدة تقوم بالتالي:

  • تحاول استدعاء API بالتوكن الحالي.
  • إذا حصلت على 401 Unauthorized، تستدعي endpoint التجديد.
  • تخزن التوكن الجديد وتعيد المحاولة أو تعيد توجيه المستخدم إذا فشل التجديد.

7. أفضل الممارسات والأمان في Django REST Next.js Authentication

لجعل النظام أكثر أمانًا واحترافية:

  • استخدم HTTPS دائمًا في بيئة الإنتاج حتى لا يمكن اعتراض التوكنات أو الكوكيز.
  • تجنب تخزين التوكنات في localStorage في المشاريع الحساسة؛ استخدم HTTP-only Cookies قدر الإمكان.
  • قم بتقييد CORS على النطاقات الموثوقة فقط.
  • فعّل قيود كلمات المرور في Django باستخدام AUTH_PASSWORD_VALIDATORS.
  • لا تنسَ إدارة الـ migrations بشكل صحيح عند تغيير نماذج المستخدمين، يمكنك مراجعة التحكم بالإصدارات في Django: إدارة migrations بشكل صحيح.

8. ملخص: من API إلى واجهة محمية

لمراجعة ما تم إنشاؤه:

  1. Django REST API:
    • Endpoints للتسجيل وتسجيل الدخول وجلب المستخدم الحالي.
    • مصادقة JWT عبر Simple JWT.
    • إعداد CORS للسماح لتطبيق Next.js بالاتصال.
  2. Next.js Frontend:
    • صفحات تسجيل وتسجيل دخول تستدعي API Django.
    • تخزين tokens (ببساطة عبر localStorage أو بشكل آمن في Cookies عبر API Routes).
    • صفحات محمية (Dashboard) تتحقق من حالة المستخدم وتستدعي endpoint /me/.
  3. أمان وإدارة جلسات:
    • استخدام access و refresh tokens.
    • تجديد التوكن عند انتهاء صلاحيته.
    • استخدام HTTPS وقيود CORS وإعدادات كلمات المرور.

بهذا تكون قد بنيت نظام Django REST Next.js Authentication متكاملًا من الصفر، يمكن تطويره لاحقًا لدعم أدوار المستخدمين (Roles)، مصادقة متعددة العوامل (MFA)، ودمجه مع بروتوكولات مثل OAuth2 أو تسجيل الدخول بدون كلمة مرور.

حول المحتوى:

شرح كامل لكيفية بناء نظام تسجيل دخول وتسجيل مستخدمين باستخدام Django REST في الخلفية و Next.js في الواجهة، بما في ذلك إدارة الجلسات وحماية الصفحات.

هل كان هذا مفيدًا لك؟

أضف تعليقك