استخدام حسابات جملة في مشروع جانغو

دعنا نقول أن الموقع الذي يستخدمه المستخدمون مكتوب في جملة ، ولكن لإنشاء منتج جديد لجمهورك ، اخترت حزمة Python / Django.


نتيجة لذلك ، تحتاج إلى استخدام حسابات المستخدمين من قاعدة بيانات جملة في جانغو.


لكن المشكلة تكمن في أن جملة و Django يستخدمان خوارزميات مختلفة لتجزئة كلمات المرور ، لذلك فشل نسخ الحسابات فقط.


بعد قراءة وثائق Django ، تجاوز سعة المكدس وقضاء بعض الوقت ، حصلت على الحل الموضح أدناه ، والذي يستخدم ممارسات التطوير الموصى بها لـ Django إلى أقصى حد.


تحذيرات


قد لا يناسبك هذا الحل المعماري ، راجع المناقشة في التعليقات .


لفهم ما يحدث في الأمثلة أدناه ، يجب أن يكون لديك بعض فهم بنية جانغو.


أفترض أيضًا أنك تعرف كيفية نشر مشروع Django ، لذلك أنا لا أصف هذه العملية.


يتم نسخ الكود من مشروع عامل ، لكن سيكون من السهل التكيف مع مشروعك بأقل تغييرات.


ربما ، في الإصدار الرئيسي التالي من Django ، قد ينتهك هذا الرمز ، ومع ذلك ، سيبقى مبدأ الحل كما هو.


في هذا الدليل ، لا أصف الواجهة الأمامية لنظام الترخيص ، حيث:


  • تعتمد الواجهة الأمامية التي لديك على احتياجات مشروعك (قد يكون حتى نقطة نهاية Json API ، على سبيل المثال)
  • هذه المعلومات موصوفة بالفعل في البرامج التعليمية الرسمية في جانغو والمقالات المبدئية المختلفة

خوارزمية


  • قم بتوصيل قاعدة بيانات جملة (DB) بمشروع Django
  • إنشاء نموذج JoomlaUser يمثل مستخدمًا من قاعدة بيانات Joomla
  • اكتب check_joomla_password() تتحقق من مطابقة كلمة المرور المدخلة لكلمة المرور الأصلية للمستخدم.
  • أضف ترخيصًا جديدًا للخلفية "Joomla Auth Backend" إلى المشروع ، والذي عند ترخيص العميل في Django ، سيحصل على حساب المستخدم من قاعدة بيانات Joomla

1. الاتصال بقاعدة بيانات جملة:


  • اقرأ كيف يعمل Django مع قواعد بيانات متعددة
  • لتوصيل قاعدة بيانات جملة بمشروع Django الخاص بنا ، أضف الكود التالي إلى ملف إعدادات المشروع /project_name/settings.py :


     DATABASES = { #    'default': { ... }, 'joomla_db': { 'ENGINE': 'django.db.backends.mysql', 'OPTIONS': {}, 'NAME': 'joomla_database_name', # Don't store passwords in the code, instead use env vars: 'USER': os.environ['joomla_db_user'], 'PASSWORD': os.environ['joomla_db_pass'], 'HOST': 'joomla_db_host, can be localhost or remote IP', 'PORT': '3306', } } 


إذا لزم الأمر ، في نفس الملف مع إعدادات المشروع ، يمكنك تمكين تسجيل استعلامات قاعدة البيانات:


 # add logging to see DB requests: LOGGING = { 'version': 1, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'level': 'DEBUG', 'handlers': ['console'], }, }, } 

2. إنشاء نموذج JoomlaUser


  • اقرأ كيف يمكن لنموذج Django استخدام قاعدة بيانات موجودة
  • فكر في مكان وضع JoomlaUser الجديد.
    في مشروعي ، قمت بإنشاء تطبيق يسمى "المستخدمين" ( manage.py startapp users ). سوف يحتوي على خلفية الترخيص ونموذج مستخدم جملة.
  • قم بإنشاء النموذج تلقائيًا باستخدام inspectdb:
    python manage.py inspectdb live_users --database="joomla_db"
    joomla_db - اسم قاعدة البيانات التي حددتها في settings.py/DATABASES ؛
    live_users - اسم الجدول مع الحسابات.
  • أضف النموذج الخاص بك إلى users/models.py :


     class JoomlaUser(models.Model): """ Represents our customer from the legacy Joomla database. """ username = models.CharField(max_length=150, primary_key=True) email = models.CharField(max_length=100) password = models.CharField(max_length=100) # you can copy more fields from `inspectdb` output, # but it's enough for the example class Meta: # joomla db user table. WARNING, your case can differs. db_table = 'live_users' # readonly managed = False # tip for the database router app_label = "joomla_users" 


بعد ذلك ، نحتاج إلى التأكد من وصول النموذج إلى قاعدة البيانات الصحيحة. للقيام بذلك ، أضف إلى المشروع جهاز توجيه للاستعلامات إلى قواعد بيانات مختلفة ، والتي ستعيد توجيه الطلبات من نموذج JoomlaUser إلى قاعدة بياناته الأصلية.


  1. قم بإنشاء الملف "db_routers.py" في المجلد الرئيسي للمشروع (في نفس المكان الذي توجد به "settings.py"):


     # project_name/db_routers.py class DbRouter: """this router makes sure that django uses legacy 'Joomla' database for models, that are stored there (JoomlaUser)""" def db_for_read(self, model, **kwargs): if model._meta.app_label == 'joomla_user': return 'joomla_db' return None def db_for_write(self, model, **kwargs): if model._meta.app_label == 'joomla_user': return 'joomla_db' return None 

  2. تسجيل جهاز توجيه جديد في settings.py :


     # ensure that Joomla users are populated from the right database: DATABASE_ROUTERS = ['project_name.db_routers.DbRouter'] 


الآن يمكنك الحصول على حساب من قاعدة البيانات القديمة.
إطلاق محطة جانغو ومحاولة سحب مستخدم حالي: python manage.py shell


 >>> from users.models import JoomlaUser >>> print(JoomlaUser.objects.get(username='someuser')) JoomlaUser object (someusername) >>> 

إذا كان كل شيء يعمل (ترى المستخدم) ، فانتقل إلى الخطوة التالية. خلاف ذلك ، انظر إلى إخراج الخطأ وتصحيح الإعدادات.


3. تحقق من كلمة مرور حساب جملة


جملة لا تخزن كلمات مرور المستخدم ، ولكن تجزئة بهم ، على سبيل المثال
$2y$10$aoZ4/bA7pe.QvjTU0R5.IeFGYrGag/THGvgKpoTk6bTz6XNkY0F2e


بدءًا من Joomla v3.2 ، يتم تشفير كلمات مرور المستخدم باستخدام خوارزمية BLOWFISH .


لذلك قمت بتنزيل رمز python باستخدام هذه الخوارزمية:


 pip install bcrypt echo bcrypt >> requirements.txt 

وأنشأت وظيفة للتحقق من كلمات المرور في users/backend.py :


 def check_joomla_password(password, hashed): """ Check if password matches the hashed password, using same hashing method (Blowfish) as Joomla >= 3.2 If you get wrong results with this function, check that the Hash starts from prefix "$2y", otherwise it is probably not a blowfish hash :return: True/False """ import bcrypt if password is None: return False # bcrypt requires byte strings password = password.encode('utf-8') hashed = hashed.encode('utf-8') return hashed == bcrypt.hashpw(password, hashed) 

انتباه! تستخدم إصدارات جملة أقل من 3.2 طريقة تجزئة مختلفة (md5 + ملح) ، لذلك لن تعمل هذه الوظيفة. في هذه الحالة ، اقرأ
مناقشة Stackoverflow وإنشاء وظيفة التحقق من التجزئة التي تبدو مثل هذا:


 # WARNING - THIS FUNCTION WAS NOT TESTED WITH REAL JOOMLA USERS # and definitely has some errors def check_old_joomla_password(password, hashed): from hashlib import md5 password = password.encode('utf-8') hashed = hashed.encode('utf-8') if password is None: return False # check carefully this part: hash, salt = hashed.split(':') return hash == md5(password+salt).hexdigest() 

لسوء الحظ ، ليس لدي قاعدة مستخدمين من الإصدار القديم من جملة في متناول اليد ، لذلك لا يمكنني اختبار هذه الميزة لك.


4. الخلفية ترخيص المستخدم جملة


أنت الآن جاهز لإنشاء خلفية جانغو لتخويل المستخدمين من مشروع جملة.


  1. قراءة كيفية تعديل نظام ترخيص جانغو


  2. سجل خلفية جديدة (غير موجودة بعد) في project/settings.py :


     AUTHENTICATION_BACKENDS = [ # Check if user already in the local DB # by using default django users backend 'django.contrib.auth.backends.ModelBackend', # If user was not found among django users, # use Joomla backend, which: # - search for user in Joomla DB # - check joomla user password # - copy joomla user into Django user. 'users.backend.JoomlaBackend', ] 

  3. قم بإنشاء خلفية ترخيص مستخدم جملة في users/backend.py



 from django.contrib.auth.models import User from .models import JoomlaUser def check_joomla_password(password, hashed): # this is a fuction, that we wrote before ... class JoomlaBackend: """ authorize users against Joomla user records """ def authenticate(self, request, username=None, password=None): """ IF joomla user exists AND password is correct: create django user return user object ELSE: return None """ try: joomla_user = JoomlaUser.objects.get(username=username) except JoomlaUser.DoesNotExist: return None if check_joomla_password(password, joomla_user.password): # Password is correct, let's create and return Django user, # identical to Joomla user: # but before let's ensure there is no same username # in DB. That could happen, when user changed password # in Joomla, but Django doesn't know that User.objects.filter(username=username).delete() return User.objects.create_user( username=username, email=joomla_user.email, password=password, # any additional fields from the Joomla user: ... ) # this method is required to match Django Auth Backend interface def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None 

يؤدي


تهانينا - يمكن الآن لمستخدمي موقع جملة الحالي استخدام بيانات اعتمادهم في موقع / تطبيق جديد.


كتفويض للمستخدمين النشطين من خلال الواجهة الجديدة ، سيتم نسخهم واحدًا تلو الآخر في قاعدة البيانات الجديدة.


بدلاً من ذلك ، قد لا ترغب في نسخ كيانات المستخدم من النظام القديم إلى النظام الجديد.


في هذه الحالة ، إليك رابط لمقال يصف كيفية استبدال طراز المستخدم الافتراضي بنموذجك في Django (نموذج JoomlaUser الموضح أعلاه).


يتم اتخاذ القرار النهائي ، سواء نقل أو عدم نقل المستخدمين ، على أساس العلاقة التي ستكون عليها المشروعات الجديدة والقديمة. على سبيل المثال ، أين سيتم تسجيل المستخدمين الجدد ، والموقع / التطبيق سيكون الرئيسي ، وما إلى ذلك.


الاختبار والتوثيق


الآن يرجى إضافة الاختبارات والوثائق المناسبة التي تغطي الرمز الجديد. يرتبط منطق هذا الحل ارتباطًا وثيقًا بهندسة جانغو وهو غير واضح جدًا ، لذا إذا لم تقم بإجراء الاختبارات / الوثائق الآن ، فسيصبح دعم المشروع أكثر تعقيدًا في المستقبل.

Source: https://habr.com/ru/post/ar442592/


All Articles