【问题标题】:Django Two tables for users authorizationDjango 用户授权两张表
【发布时间】:2019-08-30 14:04:10
【问题描述】:

我必须用 PHP 应用程序实现一个向后兼容的 Django 服务器。旧版应用使用LegacyUser 模型进行授权,或多或少类似于:

class LegacyUser(models.Model):
    id = models.BigAutoField(primary_key=True)
    email = models.CharField(max_length=255, unique=True)
    password = models.CharField(max_length=120, blank=True, null=True)
    ...
    other_data_fields
    ...

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    EMAIL_FIELD = 'email'

在新系统中,我不必添加LegacyUser 的新记录(但我可以)。

目前,旧系统不允许为每个组创建多个用户。实际上LegacyUser 应该被视为一个组,但我是作为用户实现的。

现在我必须为每个 LegacyUser 实现多个 Users,所以我添加了适当的 Django 用户进行授权,例如:

class User(AbstractUser):
    username = None
    email = models.EmailField(unique=True)
    legacy_user = models.ForeignKey(LegacyUser, on_delete=models.DO_NOTHING)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['publisher']

    class Meta:
        managed = True
        db_table = 'user'

base.py 设置中:

...
AUTH_USER_MODEL = 'api.LegacyUser'

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'AUTH_HEADER_TYPES': ('Bearer',),
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'id',
}

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend', #Supports User
    'common.auth.backends.LegacyBackend'         #Supports LegacyUser
]
...

新应用程序应该可以同时登录LegacyUserUser。授权后 LegacyUser.id 应用作 USER_ID_CLAIM。

这意味着如果我有一个 LegacyUser 喜欢:

{
    "id": 1,
    "email": admin@domain.com,
    "password": "hashed_password",
    ...
}

还有两个用户,例如

{
    "id": 1,
    "email": user1@domain.com,
    "password": "hashed_password",
    "legacy_user_id": 1,
    ...
}
{
    "id": 2,
    "email": user2@domain.com,
    "password": "hashed_password",
    "legacy_user_id": 1,
    ...
}

LegacyUser.idUser.legacy_user.id 的值应在请求中可见。

更重要的是email 字段在LegacyUserUser 中必须是唯一的

这可能有两个用户授权模型吗? AUTH_USER_MODEL 只能让我拥有一个这样的模型

AUTH_USER_MODEL = 'api.LegacyUser'

我想到的解决方案(存档向后兼容的应用程序)是将当前密码和电子邮件从 LegacyUser 复制到新模型 User 模型并将它们标记为最高权限。

还必须为新系统和旧系统同步密码,这样我就可以使用一些存储过程,在更新时更改数据库中两个地方的密码?对我来说,它闻起来很糟糕,也许有其他方法可以在不同步这些密码或仅使用两个表进行授权的情况下做到这一点?

编辑:为了解决这个问题,我创建了 MySQL 视图,其中包含仅用于授权的统一用户数据,例如:

CREATE OR REPLACE VIEW unified_user AS
    SELECT email as email, password as password, is_active as is_active, last_login as last_login
    FROM user
    UNION ALL
    SELECT email as email, password as password, 1 as is_active, null as last_login
    FROM legacy_user;

因此,随着视图的更新,密码的同步将自动完成

【问题讨论】:

  • 如果我们将所有旧用户迁移到新用户模型并提供大多数权限会出现什么问题?密码同步的原因是什么??请详细说明。
  • 这个问题是它必须向后兼容旧表。假设用户使用 LegacyUser 表在旧系统上更改密码,那么新旧系统的密码将不同步。我想避免硬迁移。我还是慢慢迁移到新系统留下旧系统的功能会更好
  • 希望我能部分解决您的问题。 PHP 和 Django 会通过 REST API 进行通信吗?从 PHP 到 django 的请求是用于旧版用户身份验证。正确的? LegacyUser.id 或 User.legacy_user.id 的值应该在请求中可见。您发布的 3 个 json 有效负载是请求有效负载。正确的?你在使用 Django REST 框架吗?
  • 旧系统和新系统都提供与前端应用程序的 REST API 通信。新系统应提供系统的旧功能添加新功能。我正在使用 Django REST 框架。更重要的是,我想为 Swagger aka yasgsimplejwt 提供基于会话的授权,以实现端点安全。旧系统和新系统之间的通信只能在数据库上进行(它们之间没有HTTP调用)新系统必须自己实现授权

标签: python django django-rest-framework django-authentication backwards-compatibility


【解决方案1】:

您需要一个标志来区分旧用户和新用户的身份验证。 让我专注于基于会话的身份验证。

AUTHENTICATION_BACKENDS = [
'common.auth.backends.CustomUserBackend'

]

然后从中间件中删除 django.contrib.auth.middleware.AuthenticationMiddleware 并放置您的 CustomAuthenticationMiddleware。

MIDDLEWARE = [
   ...
    'Your CustomAuthenticationMiddleware',
   ....
]

CustomAuthenticationMiddleware,根据会话变量附加您的用户。

CustomUserBackend 类实现这些方法。请阅读Auth

def get_user(self, user_id):
    # read is_legacy_user from session
    # Try to get user based on session is_legacy_user
    # Note that this method is called in CustomAuthenticationMiddleware middleware. 
    # and you have to read from 2 separate table. Session is your friend to differentiate which table we need to fetch.
def authenticate(self, request, email, password, is_legacy_user):
    if is_legacy_user:
       #write the logic for legacy user.
    else:
       #write the logic for new user.

从视图调用 authenticate 函数 (from django.contrib.auth import authenticate)。

对于旧用户

user = authenticate(email, password, is_legacy_user=True)
# Store the user ID in session as well as is_legacy_user value

对于新用户

authenticate(email, password, is_legacy_user=False)
# Store the user ID in session as well as is_legacy_user value

我希望这是您解决基于会话的身份验证问题的方法。如果那行得通;将转向基于令牌。

【讨论】:

    【解决方案2】:

    您想查看 [a https://docs.djangoproject.com/en/2.2/topics/auth/customizing/.您可以根据需要使用多种不同的身份验证方式。 Session 记录中存储的部分信息是成功使用了哪个身份验证后端。这有点涉及,但它们为您提供了执行几乎任何您喜欢的任何事情所需的所有控制权。

    几年前,我在一个系统上使用了它,其中主要用户/密码信息来自外部订阅管理服务器。如果用户/通行证在正常的用户身份验证系统上不起作用,我检查了另一个系统。如果成功,我会即时创建一个新用户。

    【讨论】:

      猜你喜欢
      • 2013-07-18
      • 1970-01-01
      • 2011-04-30
      • 1970-01-01
      • 1970-01-01
      • 2012-02-25
      • 1970-01-01
      • 2021-10-08
      • 2020-07-14
      相关资源
      最近更新 更多