我正在大修我最初使用 Joomla 到 Django 制作的网站,我想知道是否可以直接从 Joomla 导入用户记录(我主要关心的是用户密码,因为它们是加密的)。
4 回答
是的,你可以,但你必须做一些工作。Joomla 将用户保存在一些特定的数据库表结构中,因此您必须将它们拉出并将它们插入到您在 Django 应用程序中创建的用户表中。至于加密,如果算法是已知的,它可能是保存在数据库中的哈希值,只要您在 Django 应用程序中实现相同的哈希算法,您就可以按原样传输它。
记住:Django 是一个比 Joomla 更通用的“概念”——它是一个用于编写 Web 应用程序的框架,因此理论上你甚至可以用它完全重新实现 Joomla。
Django 中的 Joomla 用户(Django 身份验证后端,从 Joomla 填充用户)
一旦我需要在我用 Django 编写的新 API 中使用我们现有的 Joomla 用户。问题是我不能只将 Joomla 用户复制到 Django 数据库中,因为:
- Joomla 密码散列系统与 Django 不同。
- J 用户和 D 用户有不同的字段集(这很容易修复,但仍然)
所以我为 Django 制作了一个自定义的身份验证后端,现在我可以自信地说
Django 可以针对 Joomla 数据库对用户进行身份验证,而无需解密密码哈希或一次从 Joomla DB 复制所有用户。
算法:
- 将 Joomla 数据库连接到 Django 项目
- 创建 JoomlaUser 模型,从 Joomla DB 填充用户
- 实现
check_joomla_password()
功能,以与 Joomla 相同的方式验证用户密码 - 添加自定义“Joomla Auth Backend”,在第一次登录时将每个用户从 Joomla 复制到 Django
执行:
要了解发生了什么,您应该对 Django 有一些经验。必须根据您的 django 项目相应地修改代码。但是,代码是从工作项目中获取的,更改最少,并且应该很容易根据您的需要进行设置。
1.连接到Joomla DB:
- 阅读https://docs.djangoproject.com/en/dev/topics/db/multi-db/
- 添加到
/project_name/settings.py
:
DATABASES = {
'default': {"your default DB settings"},
'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.创建Joomla用户模型
- 阅读https://docs.djangoproject.com/en/2.1/howto/legacy-databases/
- 想想在哪里保留新的“Joomla 用户”模型。在我的项目中,我创建了
'users'
应用程序,我的自定义用户模型位于其中,并且将放置自定义 Joomla 后端。 - 检查用户如何存储在现有的 Joomla DB 中:
python manage.py inspectdb --database="joomla_db"
- 查找并仔细检查用户表。
- 添加到
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 模型将使用正确的数据库,请添加一个数据库路由器:
- 在项目文件夹中创建文件“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_users':
return 'joomla_db'
return None
def db_for_write(self, model, **kwargs):
if model._meta.app_label == 'joomla_users':
return 'joomla_db'
return None
- 注册新路由器,为此,添加
settings.py
:
# ensure that Joomla users are populated from the right database:
DATABASE_ROUTERS = ['project_name.db_routers.DbRouter']
现在转到 django shell./manage.py shell
并尝试填充一些用户,例如
>>> from users.models import JoomlaUser
>>> print(JoomlaUser.objects.get(username='someuser'))
JoomlaUser object (someuser)
>>>
如果一切正常 - 继续下一步。否则请查看错误、修复设置等
3.检查Joomla用户密码
Joomla 不存储用户密码,而是存储密码哈希,例如
$2y$10$aoZ4/bA7pe.QvjTU0R5.IeFGYrGag/THGvgKpoTk6bTz6XNkY0F2e
从 Joomla v3.2 开始,用户密码使用BLOWFISH算法进行哈希处理。
所以我下载了一个 python 河豚实现:
pip install bcrypt
echo bcrypt >> requirements.txt
并在以下位置创建了 Joomla 密码检查功能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 from Joomla.
: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)
旧版本警告!Joomla < 3.2 使用不同的哈希方法(md5+salt),所以这个函数不起作用。在这种情况下,读取joomla 密码加密 并在 python 中实现一个哈希检查器,它可能看起来像:
# WARNING - THIS FUNCTION 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()
不幸的是,我没有运行旧的 Joomla 实例,因此我无法为您测试此功能。
4. Joomla 身份验证后端
现在您已准备好为 Django 创建一个 Joomla 身份验证后端。
阅读如何修改 django auth 后端:https ://docs.djangoproject.com/en/dev/topics/auth/customizing/
在以下位置注册 Jango(尚不存在)后端
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',
]
- 在以下位置创建 Joomla 身份验证后端
users/backend.py
:
from django.contrib.auth.models import User
from .models import JoomlaUser
""" check password function we wrote before """
def check_joomla_password(password, hashed):
...
class JoomlaBackend:
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 identical Django user:
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
测试和文档
恭喜 - 现在您来自旧 Joomla 站点的客户可以在新的 Django 站点或 rest api 等上使用他们的凭据
现在,添加适当的测试和文档来覆盖这个新代码。它的逻辑非常棘手,所以如果您不进行测试和文档(懒惰的家伙) - 维护项目将是您(或其他人)屁股的痛苦。
亲切的问候,@ Dmytro Gierman
更新 11.04.2019 - 修复了错误。
Joomla (PHP) 是一个 CMS,而 Django (Python) 是一个 Web 框架。
我想知道这是否真的可能。我现在可以得出的结论是,这是不可能的。但是,有人可能对此有任何想法。
谢谢 :)