从PHP迁移至Django,能否实现MySQL AES加密并兼容ORM与迁移?
如何在Django中复现PHP的AES_ENCRYPT/DECRYPT逻辑并保留ORM核心特性
完全可以实现,且能完整保留Django ORM、数据迁移等核心功能,核心思路是自定义字段类型,在字段的读写环节对接MySQL的AES加密函数,具体实现步骤如下:
一、自定义加密字段
基于Django的BinaryField扩展,重写字段的入库和出库逻辑,自动调用MySQL的AES_ENCRYPT和AES_DECRYPT函数:
# myapp/fields.py from django.db import models from django.conf import settings class AESEncryptedField(models.BinaryField): def get_prep_value(self, value): # 入库时调用AES_ENCRYPT加密 if value is None: return None # 使用配置中的密钥,必须和PHP端完全一致 return models.functions.AESEncrypt(value, settings.AES_ENCRYPTION_KEY) def from_db_value(self, value, expression, connection): # 出库时调用AES_DECRYPT解密,转成字符串返回 if value is None: return None decrypted = models.functions.AESDecrypt(value, settings.AES_ENCRYPTION_KEY) return decrypted.decode('utf-8') if decrypted else None def to_python(self, value): # 处理表单提交等场景的类型转换 if isinstance(value, str): return value return super().to_python(value)
二、在模型中使用自定义字段
直接替换原有的CharField等类型,用自定义字段映射加密列:
# myapp/models.py from django.db import models from .fields import AESEncryptedField class User(models.Model): id = models.AutoField(primary_key=True) username = AESEncryptedField(max_length=255) email = AESEncryptedField(max_length=255) def __str__(self): return self.username
三、配置加密密钥
在settings.py中添加密钥,确保和PHP端使用的密钥完全一致:
# settings.py AES_ENCRYPTION_KEY = "你的加密密钥"
四、关键注意事项
- 字段类型兼容:PHP中
AES_ENCRYPT返回二进制数据,因此Django字段基于BinaryField,迁移时会自动创建对应数据库列(若迁移现有数据,需确保原列是BLOB或VARBINARY类型)。 - 加密字段查询:若需基于加密字段过滤,不能直接用常规查询,需结合
annotate和AESDecrypt函数:from django.db.models.functions import AESDecrypt from django.conf import settings # 过滤邮箱为user@example.com的用户 users = User.objects.annotate( decrypted_email=AESDecrypt('email', settings.AES_ENCRYPTION_KEY) ).filter(decrypted_email='user@example.com') - 字符集一致性:确保MySQL数据库字符集与PHP端一致(如
utf8mb4),避免解密后出现乱码。
五、数据迁移
Django迁移系统会自动识别自定义字段,执行python manage.py makemigrations和python manage.py migrate即可完成表结构创建/更新。若迁移现有加密数据,直接将原表二进制数据导入对应列即可,无需额外处理。
内容的提问来源于stack exchange,提问作者Elex Promise
相关产品推荐
相关产品推荐

