Table of Contents
تصميم نظام موحّد للتوثيق مع نمط منهجية المصانع في دجانغو
ويطالب تطبيقات شبكة الإنترنت الحديثة بنظم التوثيق المرنة والمتدرجة على السواء، ويتوقع المستخدمون أن يسجلوا استخدام البريد الإلكتروني وكلمة السر، والحسابات الاجتماعية، وبروتوكولات الإشارة الوحيدة، أو الأساليب القائمة على الكين، التي كثيرا ما تكون في نفس التطبيق، وفي حين أن نظام التحقق القائم على أساس الجاهزة الذي وضعه جانغو يدعم عدة مساندات من خلال وضع ، فإن نهج التشكيلة هذا ثابت ويتطلب تغييرا.
وللتغلب على هذه القيود، يتجه العديد من المطورين إلى تصميم أنماط مثل طريقة المصانع، وهذا النمط الإبداعي يوفر طريقة نظيفة وموضوعية لحصر المساندة الفورية للتوثيق، مما يتيح للنظام التكيف في وقت العمل دون أن يربط مدونة العملاء بالطبقات المحددة، وفي هذه المادة، ستتعلمون كيفية تنفيذ نظام التوثيق النموذجي في دجانغو باستخدام نمط منهجية المصانع، مع استكمال أفضل الممارسات في العالم الحقيقي.
وسنستكشف المفاهيم الأساسية وراء طريقة المصانع، ونبني صفوفا محددة للتوثيق لعدة استراتيجيات مشتركة )اسم المستخدم/الكلمة السدودية، OAuth2, JWT، والشعار الاجتماعي(، ونبني مصنعا يختار المساندة المناسبة استنادا إلى المدخلات أو التشكيل، وفي النهاية، سيكون لديك هيكل قابل للعادة يجعل من التقريبي إضافة أساليب جديدة للتوثيق مع الحفاظ على بقية قاعدتك الشفرة مستقرة.
فهم نمط المصانع
ويعرّف نمط طريقة المصانع واجهة لإيجاد جسم ما، ولكن يسمح للطبقات الفرعية بأن تقرر أي فئة تُحدّد فوراً، وهي تنتمي إلى فئة أنماط التصميم الإبداعية، وهي مفيدة بصفة خاصة عندما لا يمكن للفئة أن تتوقع نوع الأشياء التي تحتاج إليها أو عندما تريد أن تحدد صنفاتها الفرعية ما تُنشئه.
وفي سياق التوثيق، يتيح لك النمط تحديد واجهة مشتركة لجميع أساليب التوثيق )مثلا، طريقة ][ ثم وضع تنفيذات ملموسة لكل طريقة مدعمة، وبدلا من التكرير الجاد الذي يدعم الاستخدام، تفوض القرار إلى فئة مصنعية تعيد المطبعة الصحيحة القائمة على بارامترات زمنية، وهذا النهج يعزز أساليب الفرز الجديدة المفتوحة/المغلقة دون أن تضيف الحجية:
وطريقة المصنع تختلف عن طريقة بسيطة (طريقة ثابتة تختار طبقة) من حيث أنها تعتمد عادة على تصنيف فرعي لتختلف الجسم المخلق، غير أن طريقة مصنع ثابت في بيتون وجانغو، تعود في كثير من الأحيان إلى طبقة فرعية مناسبة، هي طريقة كافية وأنظف، كما ترى أدناه، وما إذا كانت تسميها طريقة مصنع أو مصنع ثابت، تظل فوائد العملاء من فصول ديكو.
For a deeper explanation of the pattern, refer to Refactoring Guru’s Factory Method guide].
تنفيذ خطة عمل جانغو
وسنبدأ ببناء وحدة توثيقية محدودة الإنتاج، وإن كانت جاهزة للإنتاج، وقد يبدو هيكل المشروع كذلك:
myproject/
authfactory/
__init__.py
base_auth.py
backends.py
factory.py
views.py
templates/
settings.py
فئة التوثيق
إنشاء طبقة قاعدية مختصرة تحدد الواجهة بين كل دعم ملموس يجب أن تنفذه، وستتضمن هذه الوصلة على الأقل طريقة ، ولكن يمكنك أيضا إضافة خطاف اختيارية مثل أو أساليب تجهيز ما بعد التصويت.
# authfactory/base_auth.py
from abc import ABC, abstractmethod
class BaseAuthMethod(ABC):
"""Common interface for all authentication strategies."""
@abstractmethod
def authenticate(self, request):
"""
Authenticate the user from the given request.
Must return a User instance on success, or None on failure.
"""
pass
def get_user(self, user_id):
"""
Optional method to retrieve a user object by ID.
Can be used by backends that support session restoration.
"""
return None
(أ) أن يُكفل، باستخدام ، أن تنفذ أي فئة فرعية أو أن تُنشئ شركة بيتون في الوقت الراهن، مما يجعل العقد واضحا ويساعد على التشويه.
المساندة للتوثيق
والآن، نطبق دروسا محددة لأشد أساليب التوثيق شيوعا، وسنشمل ما يلي:
- مصداقية المستخدم/الكلمة السريــة (استخدام دجانغو المبني في )
- التصديق على أساس OAuth2 (مثال مستفيض)
- تصديق شركة JSON Web Token (JWT) على وثائق عملاء المنظمة
- تسجيل اجتماعي عبر جانغو لاوث
اسم المستخدم/الكلمة الخلفية
ويعود هذا المندوبون إلى نظام التوثيق الخاص بجانغو، الذي يختبر في المعركة ويشمل تهجير كلمة السر، والهدر، وغير ذلك من السمات الأمنية.
# authfactory/backends.py
from django.contrib.auth import authenticate
from .base_auth import BaseAuthMethod
class UsernamePasswordAuth(BaseAuthMethod):
def authenticate(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
return authenticate(request, username=username, password=password)
def get_user(self, user_id):
from django.contrib.auth import get_user_model
User = get_user_model()
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
OAuth2 Backend
إن تدفقات الأوث2 أكثر تعقيداً، وهذا المثال يبين كيف يمكن أن تصادق على وصول مُقدّم من طرف ثالث.
# authfactory/backends.py (continued)
import requests
from django.contrib.auth import get_user_model
from .base_auth import BaseAuthMethod
class OAuth2Auth(BaseAuthMethod):
def __init__(self, provider_token_url, userinfo_url, client_id):
self.provider_token_url = provider_token_url
self.userinfo_url = userinfo_url
self.client_id = client_id
def authenticate(self, request):
access_token = request.POST.get('access_token') or \
request.META.get('HTTP_AUTHORIZATION', '').replace('Bearer ', '')
if not access_token:
return None
# Verify token with the provider's introspection endpoint (simplified)
response = requests.get(
self.userinfo_url,
headers={'Authorization': f'Bearer {access_token}'}
)
if response.status_code != 200:
return None
user_info = response.json()
email = user_info.get('email')
if not email:
return None
User = get_user_model()
user, _ = User.objects.get_or_create(
email=email,
defaults={'username': email.split('@')[0]}
)
return user
def get_user(self, user_id):
User = get_user_model()
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
ملاحظة أن دعمات الإنتاج OAuth2 ينبغي أن تتحقق أيضاً من التوقيع المكسور، أو التحقق من نهايته، وربما التحقق من مطالبة ]، وأن التنفيذ القوي سيستخدم مكتبة مثل أو .
JWT Backend (for REST API)
وعند بناء نظام تقييم الأداء مع إطار عمل دجانغو للإنقاذ، كثيرا ما تحتاجون إلى توثيق هوية المستخدمين عن طريق شبكة يو جون ويب توكس، وتثبت المساندة التالية صحة نظام JWT وتسترد المستخدم من الحمولة.
# authfactory/backends.py (continued)
import jwt
from django.conf import settings
from django.contrib.auth import get_user_model
from .base_auth import BaseAuthMethod
class JWTAuth(BaseAuthMethod):
def __init__(self, secret_key=None, algorithm='HS256'):
self.secret_key = secret_key or settings.SECRET_KEY
self.algorithm = algorithm
def authenticate(self, request):
token = request.META.get('HTTP_AUTHORIZATION', '').replace('Bearer ', '')
if not token:
return None
try:
payload = jwt.decode(
token,
self.secret_key,
algorithms=[self.algorithm]
)
except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
return None
user_id = payload.get('user_id')
if not user_id:
return None
User = get_user_model()
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
def get_user(self, user_id):
User = get_user_model()
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
اللوجين الاجتماعي عبر جانغو لاوث
وإذا استخدمتم ](FLT:16])[ للتوثيق الاجتماعي، يمكنكم أن تلفوها داخل مصنع خلفي، وهذا المثال يفترض أن تدفق قطع الأشجار الاجتماعية يُعالج بآراء جميع البعث؛ وسيُدعى إلى دعم المصنع بعد عودة مكتب التفتيش العام لإكمال الشعار.
# authfactory/backends.py (continued)
from allauth.socialaccount.models import SocialLogin, SocialAccount
from django.contrib.auth import get_user_model
from .base_auth import BaseAuthMethod
class SocialAuthBackend(BaseAuthMethod):
def __init__(self, provider):
self.provider = provider
def authenticate(self, request):
# This is called after allauth's social login process.
# The user object is typically stored in the request by allauth.
if hasattr(request, 'user') and request.user.is_authenticated:
return request.user
# Alternatively, you could inspect the session for a social token.
return None
def get_user(self, user_id):
User = get_user_model()
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
وهذه المساندة بسيطة عن قصد؛ وسيعالج الإدماج الكامل آلية الدخول الاجتماعي التي تديرها السلطعون، والنقطة الرئيسية هي أن كل دعم يتفق مع الواجهة المشتركة .]
"الفصل المُحدّد"
ويقرر صنف المصنع أي دعم ملموس للإنذار، ويمكنه استخدام سلسلة بسيطة ] أو مسح للقاموس من أجل الاستمرار، كما يمكننا أن نسمح بالتشكيل من بيئات جانغو.
# authfactory/factory.py
from django.conf import settings
from .backends import (
UsernamePasswordAuth,
OAuth2Auth,
JWTAuth,
SocialAuthBackend,
)
class AuthMethodFactory:
"""Factory that returns the appropriate authentication backend."""
_backends = {
'username_password': UsernamePasswordAuth,
'oauth2': lambda: OAuth2Auth(
provider_token_url=settings.OAUTH2_TOKEN_URL,
userinfo_url=settings.OAUTH2_USERINFO_URL,
client_id=settings.OAUTH2_CLIENT_ID,
),
'jwt': lambda: JWTAuth(
secret_key=settings.JWT_SECRET_KEY,
algorithm=settings.JWT_ALGORITHM,
),
'social': lambda: SocialAuthBackend(provider='google'),
}
@classmethod
def get_backend(cls, method_type, **kwargs):
"""
Return an instance of the authentication backend
identified by `method_type`.
"""
if method_type not in cls._backends:
raise ValueError(f"Unknown authentication method: {method_type}")
backend_creator = cls._backends[method_type]
if callable(backend_creator):
return backend_creator()
return backend_creator()
@classmethod
def get_backend_names(cls):
"""Return a list of all registered backend names."""
return list(cls._backends.keys())
ويستخدم هذا التنفيذ قاموساً للحمى للمساندة الفورية التي تتطلب حجج البناء، كما يمكن لطريقة أن تقبل حجج إضافية للكلمات الرئيسية إذا ما أردت تجاوز المعايير الافتراضية لطلب معين (مثلاً، مقدم مختلف).
وحتى من أجل زيادة المرونة، يمكنك تخزين التشكيلة الخلفية في قاعدة البيانات وتسجيلها ديناميكيا، غير أن رسم الخرائط الثابتة غالبا ما يكون كافيا ويسهل اختباره.
استخدام المصانع في الآراء والوسط
الآن ادمج المصنع في آراءك الخاصة بـ (جانغو) يجب على العميل (المستهلك أو المستهلك) أن يخبر الخادم عن طريقة التوثيق التي يعتزم استخدامها، ويمكن القيام بذلك عن طريق مظلة استفسار أو ميدان من نوع (PPOST) أو عن طريق رئيس شركة HTTP.
تقليدي فيو
# authfactory/views.py
from django.contrib.auth import login
from django.http import HttpResponse, Http404
from django.views.decorators.csrf import csrf_exempt
import json
from .factory import AuthMethodFactory
@csrf_exempt
def login_view(request):
"""
Login endpoint that supports multiple authentication methods.
Expects a JSON body with 'auth_type' and method-specific credentials.
"""
if request.method != 'POST':
return HttpResponse(status=405, content='Method not allowed')
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return HttpResponse(status=400, content='Invalid JSON')
auth_type = data.get('auth_type', 'username_password')
try:
backend = AuthMethodFactory.get_backend(auth_type)
except ValueError as e:
return HttpResponse(status=400, content=str(e))
user = backend.authenticate(request)
if user is not None:
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
return HttpResponse('Login successful')
else:
return HttpResponse(status=401, content='Invalid credentials')
ملاحظة: تتطلب وظيفة بارامتر خط احتياطي، وفي تطبيق حقيقي، إما تخزين المسار الخلفي في الدورة أو استخدام طبقة المؤخرة في المصنع لإخراج المسار تلقائياً، وللبساطة، دقنا هنا؛ وفي الإنتاج، يمكن أن ترسم كل مصنع ظهراً لخيط توثيقي.
API Views with Django REST Framework
وإذا ما عرضتم مذكرة تفاهم، يمكنكم تكييف نمط المصنع لاستخدامه مع فئات التوثيق في إطار نموذج الإبلاغ الموحد، وبدلا من إيجاد وجهة نظر منفصلة، تكتبون فئة توثيقية تُعنى بالعادة وتُوفد إلى المصنع.
# authfactory/rest_auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .factory import AuthMethodFactory
class FactoryBackendAuth(BaseAuthentication):
"""
DRF authentication class that uses the AuthMethodFactory
to validate tokens. The 'auth_type' is derived from a custom
header 'X-Auth-Type'.
"""
def authenticate(self, request):
auth_type = request.META.get('HTTP_X_AUTH_TYPE', 'jwt')
try:
backend = AuthMethodFactory.get_backend(auth_type)
except ValueError:
raise AuthenticationFailed('Unsupported authentication type')
user = backend.authenticate(request)
if user is None:
raise AuthenticationFailed('Invalid token')
return (user, None)
def authenticate_header(self, request):
return 'Bearer' # Generic challenge for any method
ثمّ يُضيفُ هذا النوعِ التوثيقِ إلى أطرِ إدارةِ إدارةِ الإستدلالِ الخاصِ بكَ أَو وجهة نظر:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'authfactory.rest_auth.FactoryBackendAuth',
# other classes can be kept as fallback
],
}
برنامج " غلوب " لاختيار المساندة الآلية
أحياناً عليك أن تختار تلقائياً دعماً يستند إلى خصائص الطلب (مثلاً، وكيل المستعمل، IP، المجال)، يمكنك كتابة الإطارات المتوسطة التي تلف الطلب وتحقن المساندة المناسبة إلى .
# authfactory/middleware.py
from .factory import AuthMethodFactory
class AutoAuthBackendMiddleware:
"""
Middleware that selects an authentication backend based on
the request path or host.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Decide on auth type - example: use 'oauth2' for /api/v2/auth/*
path = request.path_info
if path.startswith('/api/v2/auth/'):
request.auth_type = 'oauth2'
elif path.startswith('/api/v1/auth/'):
request.auth_type = 'jwt'
else:
request.auth_type = 'username_password'
return self.get_response(request)
ويمكنكم بعد ذلك استخدام في وجهات نظركم دون أن تشترطوا على العميل أن يحددها.
الاعتبارات المسبقة
التملك ومعالجة الأخطاء
وتحتاج نظم التوثيق الإنتاجي إلى قطع الأشجار بقوة، إضافة قطع الأشجار المنظم داخل كل خلفياته، والمصنع لاستخلاص المحاولات والتوثيق، والفشل، والأحداث الأمنية المحتملة.
import logging
logger = logging.getLogger(__name__)
class JWTAuth(BaseAuthMethod):
def authenticate(self, request):
# ... validation ...
if not token:
logger.warning('JWT auth attempted with no token')
return None
try:
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
except jwt.ExpiredSignatureError:
logger.info('Expired JWT token')
return None
except jwt.InvalidTokenError:
logger.warning('Invalid JWT token')
return None
# ... user retrieval ...
if user is None:
logger.error(f'JWT valid but user {payload.get("user_id")} not found')
return user
اختبار المصانع والمساندة
وينبغي اختبار كل تخلف بمعزل عن الآخر، واستخدام طلبات العميل أو السخرة من شركة ديانغو، ومقابل المصنع، اختبار أنها تعيد النوع الصحيح لكل طريقة مسجلة وترفع إلى غير معروف.
# tests/test_auth.py
from django.test import TestCase
from unittest.mock import Mock
from authfactory.factory import AuthMethodFactory
from authfactory.backends import UsernamePasswordAuth, OAuth2Auth
class FactoryTest(TestCase):
def test_get_username_password_backend(self):
backend = AuthMethodFactory.get_backend('username_password')
self.assertIsInstance(backend, UsernamePasswordAuth)
def test_get_oauth2_backend(self):
backend = AuthMethodFactory.get_backend('oauth2')
self.assertIsInstance(backend, OAuth2Auth)
def test_unknown_method_raises_error(self):
with self.assertRaises(ValueError):
AuthMethodFactory.get_backend('unknown')
class UsernamePasswordAuthTest(TestCase):
def test_authenticate_with_valid_credentials(self):
# Create a test user
from django.contrib.auth import get_user_model
User = get_user_model()
user = User.objects.create_user(username='test', password='secret')
# ... mock request ...
request = Mock()
request.POST = {'username': 'test', 'password': 'secret'}
backend = UsernamePasswordAuth()
result = backend.authenticate(request)
self.assertEqual(result, user)
توسيع النظام
لإضافة طريقة جديدة للتوثيق (مثلاً، (سام إل، حلقة سحرية، ويب-أوثن)، عليك فقط أن:
- Create a new class that inherits from and implements .
- قيده في [القاموس] في .]
- إضافة دخول تشكيلي في بيئات جانغو.
هذا الحد الأدنى من البصمات يجعل النظام سهل التمسك به و الاختبار يمكنك أيضاً أن تحزم كل دعم كتطبيق منفصل قابل للإستعمال
فوائد استخدام نمط المصانع للتوثيق
- Modularity:] Each authentication method is encapsulated in its own class, making the codebase easier to navigate and reason about.
- Scalability:] Adding a new authentication strategy does not require changes to existing views, URLs, or business logical.
- Open/Closed Principle:] The core authentication infrastructure is closed for modification but open for extension through new backend classes.
- testingability:] Backends can be unit —tested independently. Mocking the factory allows you to test views without real authentication dependencies.
- Configurability:] The factory can be driven by settings, database records, or runtime parameters, allowing different deployment environments to use different authentication methods.
- Separation of Concerns:] Authentication logical is removed from views, making views lighter and more focused on request handling.
خاتمة
إن تصميم نظام للتوثيق النموذجي مع نمط المصانع في جنغو يحول قطعة أساسية احتكارية تقليديا إلى عنصر مرن ومكثف، وبتحديد واجهة بسيطة ومساندة ملموسة لكل استراتيجية للتوثيق، فإنكم تكتسبون القدرة على تبادل أو إضافة أساليب التوثيق دون لمس بقية تطبيقكم، وتركيز المصنع على منطق التوثيق الفوري، ويدمج النمط القائم في إطار التوثيق.
ولا يقتصر هذا النهج على التوثيق؛ ويمكن تطبيق نمط طريقة المصانع نفسه على مجالات أخرى من مشروع دجانغو، مثل بوابات الدفع أو قنوات الإخطار أو مستوردي البيانات، وبما أن تطبيقك ينمو، فإن النمط يساعدك على الحفاظ على حدود نظيفة ويبقي قاعدتك الرمزية قابلة للتكيف مع المتطلبات المستقبلية.
For further reading on authentication best practices in Django, consult the official Django authentication documentation]. To explore more advanced token-based authentication, see the ] Simple JWT guide for DRF. and for a deep dive into design patterns, :