【问题标题】:Django Admin encrypt values of certain fields on changed(or save) with save_model()Django Admin 使用 save_model() 在更改(或保存)时加密某些字段的值
【发布时间】:2020-04-15 09:10:29
【问题描述】:

我目前有一个自定义用户模型,其中一些字段应该被加密(数据端),但在 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 admin change view时查看加密数据,得到以下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

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

希望有人可以帮助我

【问题讨论】:

    标签: python django encryption django-admin


    【解决方案1】:

    您在不正确的抽象层上执行此操作。

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

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

    【讨论】:

    • 使用自定义模型字段是个好主意,但这不是我想要的,因为将来我可能也会使用解密值。如果字段在 django admin 上保存或更改,我还想更改值,这是我将来会经常使用的功能
    • @LinhNguyen 就是这个想法。自定义字段将与 django admin 一起使用,无需任何更改。
    • 我会在下一个项目中这样做,我只是想要一个解决 django admin 值变化的解决方案(可能不是加密/解密方式,而是在保存时格式化某些字段)因为我打算以后用它
    • django-cryptographic-fields 自 2016 年以来一直没有更新。此外,它使用了已弃用的 keyczar。
    【解决方案2】:

    在多次尝试修改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
    

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

    【讨论】:

      猜你喜欢
      • 2013-08-02
      • 2019-04-23
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 2013-08-28
      • 2014-12-06
      • 1970-01-01
      相关资源
      最近更新 更多