حول المحتوى:
شرح كامل لكيفية بناء نظام تسجيل دخول وتسجيل مستخدمين باستخدام Django REST في الخلفية و Next.js في الواجهة، بما في ذلك إدارة الجلسات وحماية الصفحات.
في هذا الدليل العملي سنتعرف خطوة بخطوة على كيفية بناء نظام تسجيل مستخدمين وتسجيل دخول متكامل باستخدام Django REST Framework في الخلفية (Backend) و Next.js في الواجهة (Frontend)، مع التركيز على:
إذا كنت مهتمًا بالمصادقة المتقدمة مثل OAuth2 أو أنظمة تسجيل الدخول بدون كلمة مرور، يمكنك لاحقًا الاطلاع على: تنفيذ OAuth2 في Django و FastAPI و بناء نظام تسجيل دخول بدون كلمات مرور باستخدام Magic Links.
نحن نبني نظامًا من جزئين:
هناك خياران رئيسيان للمصادقة في هذه التوليفة:
في هذا الشرح سنركز على JWT لأنه شائع جدًا مع واجهات SPA و Next.js، وسهل الدمج مع حماية الصفحات.
ابدأ بإنشاء بيئة عمل وتثبيت الحزم الأساسية:
pip install django djangorestframework djangorestframework-simplejwt أنشئ مشروع وتطبيق Django:
django-admin startproject backend
cd backend
python manage.py startapp accounts في 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',
),
} لاستخدام 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',),
} يمكنك استخدام النموذج الافتراضي django.contrib.auth.models.User أو إنشاء نموذج مخصص. للبساطة سنستخدم النموذج الافتراضي.
في 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 في 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 بدون الكثير من الكود.
في 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 تتضمن:
لكي يتمكن تطبيق 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 مع أمثلة وأفضل الممارسات.
باستخدام TypeScript اختيارياً:
npx create-next-app@latest frontend
cd frontend يمكن استخدام fetch أو axios، في المثال سنستخدم fetch المدمج.
سنحتاج إلى:
/api/accounts/register/./api/accounts/token/. بسبب تعقيد إدارة الـ HTTP-only Cookies، سنعرض الفكرة العامة: Next.js يستقبل التوكنات من Django ويخزنها في Cookie عبر API Routes داخل Next.js، وليس مباشرة في localStorage من الـ Client.
افترض أنك تستخدم هيكل الصفحات التقليدي (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>
);
} في مستوى الإنتاج يفضل استخدام 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>
);
} ستحتاج إلى إرسال الـ 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>
);
} الحماية على مستوى الخادم في Next.js (خصوصًا في وضع SSR) أكثر أمانًا، لأنها تمنع تحميل الصفحة من الأساس إذا لم يكن المستخدم مصدقًا. يمكن استخدام getServerSideProps لقراءة الـ token من Cookie والتحقق منه عبر Django.
الفكرة:
getServerSideProps تقرأ هذه الـ Cookies وترسلها إلى Django للتحقق.ذلك يتطلب طبقة إضافية في Next.js (API Routes) لتغليف طلبات المصادقة، لكنه يوفر أمانًا أعلى ويفصل الـ tokens عن JavaScript في المتصفح قدر الإمكان.
في إعدادنا مع Django REST Simple JWT:
عندما ينتهي access token، يمكن للـ Frontend إرسال:
POST /api/accounts/token/refresh/
{
"refresh": "<refresh_token_here>"
} ويستقبل access token جديد. في Next.js يمكنك إنشاء دالة مساعدة تقوم بالتالي:
لجعل النظام أكثر أمانًا واحترافية:
AUTH_PASSWORD_VALIDATORS.لمراجعة ما تم إنشاؤه:
/me/.بهذا تكون قد بنيت نظام Django REST Next.js Authentication متكاملًا من الصفر، يمكن تطويره لاحقًا لدعم أدوار المستخدمين (Roles)، مصادقة متعددة العوامل (MFA)، ودمجه مع بروتوكولات مثل OAuth2 أو تسجيل الدخول بدون كلمة مرور.
شرح كامل لكيفية بناء نظام تسجيل دخول وتسجيل مستخدمين باستخدام Django REST في الخلفية و Next.js في الواجهة، بما في ذلك إدارة الجلسات وحماية الصفحات.
مساحة اعلانية