Django Admin 使用 save_model() 在更改(或保存)时加密某些字段的值

Django Admin encrypt values of certain fields on changed(or save) with save_model()

我目前有一个自定义用户模型,其中一些字段应该被加密(数据端),但在 Django Admin 上,它们需要被解密以显示实际数据,所以我有 encrypt_string()decrypt_string() 函数来处理它。

我的用户模型:

class User(AbstractUser):
    uid = models.CharField(
      "uid", max_length=255, null=True, blank=True)
    nickname = models.CharField(
        "Nickname", max_length=255, null=True, blank=True)
    
    eth_address = models.CharField(
        "Eth address", max_length=255, null=True, blank=True)
    eth_private_key = models.CharField(
        "Eth private key", max_length=255, null=True, blank=True)
    evt_address = models.CharField(
        "Evt address", max_length=255, null=True, blank=True)
    evt_private_key = models.CharField(
        "Evt private key", max_length=255, null=True, blank=True)

eth_address、eth_private_key、evt_address 和 evt_private_key 在进行任何类型的保存或编辑时都需要加密

当前,当我的服务器从 api 发出请求时,它将创建一个默认用户(在 django-rest-framework 的自定义 authentication_class 上处理):

existed_user_check = User.objects.filter(uid=uid).exists()

    if not existed_user_check:
        random_password = ''.join(["{}".format(randint(0, 9)) for num in range(0, 6)])
        eth_address, eth_private_key = generate_eth()
        evt_address, evt_private_key = generate_evt()
        new_user = User(
            username=uid, 
            uid=uid, 
            eth_address=encrypt_string(eth_address), 
            eth_private_key=encrypt_string(eth_private_key), 
            evt_address=encrypt_string(evt_address), 
            evt_private_key=encrypt_string(evt_private_key)
        )
        new_user.set_password(random_password)
        new_user.save()

在我的 Django Admin 中,我在 admin.py 中编写了以下内容来处理解密(在更改视图中显示数据时)和加密(在对这 4 个字段进行任何保存或编辑时):

user_profile_encrypt_fields = [
    'eth_address',
    'eth_private_key',
    'evt_address',
    'evt_private_key'
]
class UserAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(UserAdminForm, self).__init__(*args, **kwargs)
        for field in user_profile_encrypt_fields:
            if getattr(self.instance, field) is not None:
                try:
                    self.initial[field] = decrypt_string(getattr(self.instance, field))
                except Exception as e:
                    pass
        

class UserAdminCustom(admin.ModelAdmin):
    # UserAdminForm handle decrypt data
    form = UserAdminForm
    exclude = ('password', 'last_login', 'is_superuser', 'is_staff', 'groups',
               'user_permissions', 'username', 'first_name', 'last_name', 'is_active', 'date_joined')

    def get_queryset(self, request):
        qs = super(UserAdminCustom, self).get_queryset(request)
        return qs.filter(is_staff=False)

    def get_readonly_fields(self, request, obj=None):
        return ('id', 'created', 'modified')
    
    # save_model() handle encrypt data on saving
    def save_model(self, request, obj, form, change):
        for field in form.changed_data:
            if form.cleaned_data.get(field) is not None:
                if field in user_profile_encrypt_fields:
                    encrypted_value = encrypt_string(form.cleaned_data.get(field))
                    setattr(obj, field, encrypted_value)
        obj.save()


admin.site.register(User, UserAdminCustom)

为我自定义 UserAdminForm 来解密数据,以便管理员在进行编辑或检查时可以看到它是什么。 解密后的数据能够正确显示在更改视图中

save_model() 将在管理员编辑 Django Admin 更改视图上的字段时处理数据加密。

我目前的问题:

备注:

这些字段中的数据已经存在,所以我只讨论 django admin 上的编辑对象部分。

当我尝试编辑任何字段并保存它(应该加密)时,该字段被加密并保存,但它下面的字段(它的实际加密数据)现在变成了它的实际数据(不是加密)

在更改视图上编辑 evt_address 并保存后

使用时解密数据form = UserAdminForm

在 UserAdminCustom 上注释掉 # form = UserAdminForm 以便显示实际数据

我打印出 getattr(self.instance, field) 以查看加载 django 管理更改视图时的加密数据,并得到以下 4 个加密字段:

web_1         | eth_address : gAAAAABeAcoI08bH2fQqJboZFxg6xn5RCxdRopllS6fDeyRmsC3qzsTXo88NVYOb58eeX5IXQpxqcGhbLr8wRRoWSKQsX5vLbPPhmWqUiqf0XYQvdWUhhgYxxMwwqgEOwU2OtJfkZ0p6
web_1         | eth_private_key : gAAAAABeAcoIp1V9sKNnL-dO-fWH1W1oM7hbqky44aRLchmTtvckaaZdaKuXo1xIIozx3xl40Y2Ct3YAyCOfkJJranKgTNDcVhndYdu5-awOuYpPCJKkSSia7IP_gWjLE91Gh8vsGnkn1iEkrLaho2ff0vVHS1QgGaJxji5m7cwCk0tqSp2AIeA=
web_1         | evt_address : gAAAAABeAcoIM3rywSgAPL611WUoWLJ9mqIgUHhZn8KDQd9hi9xHzgRri0EkoS_yBvwQyzdH72RWJRsDCs38oF9P8HHW_wFDWQ==
web_1         | evt_private_key : 1814067

所以我的 evt_private_key 数据在我编辑 evt_address 值并保存

后更改为 1814067

为什么会这样,我很困惑是什么原因造成的。

希望有人能帮助我

您在错误的抽象层上进行操作。

请按照本指南创建将为您处理加密/解密的自定义 Django 字段:https://docs.djangoproject.com/en/2.2/howto/custom-model-fields/

或者只使用一些现有的解决方案,例如:https://github.com/foundertherapy/django-cryptographic-fields

在多次尝试修改 save_model 函数后,我意识到表单的数据实际上来自 UserAdminForm 所以为了能够修改 django 管理表单上的数据,我需要从保存前自行形成

class UserAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(UserAdminForm, self).__init__(*args, **kwargs)
        for field in user_profile_encrypt_fields:
            if getattr(self.instance, field) is not None:
                try:
                    self.initial[field] = decrypt_string(getattr(self.instance, field))
                except:
                    pass

    def clean_eth_address(self):
        if self.cleaned_data['eth_address'] is not None:
            data = encrypt_string(self.cleaned_data['eth_address'])
        else:
            data = None

        return data

    def clean_eth_private_key(self):
        if self.cleaned_data['eth_private_key'] is not None:
            data = encrypt_string(self.cleaned_data['eth_private_key'])
        else:
            data = None

        return data

    def clean_evt_address(self):
        if self.cleaned_data['evt_address'] is not None:
            data = encrypt_string(self.cleaned_data['evt_address'])
        else:
            data = None

        return data

    def clean_evt_private_key(self):
        if self.cleaned_data['evt_private_key'] is not None:
            data = encrypt_string(self.cleaned_data['evt_private_key'])
        else:
            data = None

        return data

现在,每当我在管理表单上进行更改时,它都会被加密