应将敏感字段单独拆出并用加密字段存储,jsonfield仅存非敏感结构化数据。mysql jsonfield不支持加密,整体加密会破坏查询能力;需用fernet实现端到端加解密,配合charfield(max_length=44),避免默认值与索引滥用。

不能直接把敏感数据塞进 JSONField 里再整体加密——那样会彻底废掉 MySQL 的 JSON 查询能力,也违背了 JSON 字段的设计本意。真正安全又可用的做法,是分层处理:结构化字段用原生 JSONField,敏感值单独抽出来走加密字段。
MySQL 的 JSONField 本身不提供加密能力
MySQL 的 JSON 类型只是存储和解析格式,不加密、不混淆、不访问控制。Django 的 models.JSONField 在 MySQL 5.7+ 上映射为原生 JSON 列,读写都走标准 JSON 函数(如 JSON_EXTRACT),底层数据明文落盘。你无法靠“开启某个选项”让这个字段自动加密。
- 试图给整个
JSONField加密(比如用自定义Field包一层)会导致所有双下划线查询(data__user__email)失效,因为 ORM 拿到的是密文字符串,不是可解析的 JSON 对象 -
JSON_CONTAINS、JSON_LENGTH、路径提取等操作全部瘫痪 - 数据库备份、binlog、从库同步看到的仍是明文 JSON —— 加密只在 Django 层做没意义
敏感字段必须拆出来,用加密专用 Field
真正要保护的不是“JSON 这个容器”,而是里面的具体值,比如 id_card、phone、bank_account。这些字段不该藏在 JSON 里,而应独立建模,用继承 models.Field 的加密字段实现端到端加解密。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 用
cryptography.fernet.Fernet实现,密钥从os.environ.get("ENCRYPTION_KEY")读取,绝不可硬编码 - 重写
get_prep_value()(存前加密)、from_db_value()(读后解密)、to_python()(表单/反序列化时解密) - 字段类型推荐
CharField(max_length=44)(Fernet 密文 base64 后长度固定),别用TextField或BinaryField - 不要给加密字段设
default—— 加密逻辑不支持默认值生成
JSONField + 加密字段混合建模才是正解
把稳定结构和敏感内容分开:JSON 存动态、非敏感元数据(如配置项、日志上下文、UI 布局),加密字段存身份证号、手机号、token 等强敏感项。模型长这样才既安全又可查:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# 非敏感 JSON:用户偏好、设备列表、通知设置
preferences = models.JSONField(default=dict)
# 敏感字段:单独加密,支持 ORM 查询(filter(encrypted_phone__icontains=...) 仍有效)
encrypted_phone = EncryptedCharField()
encrypted_id_card = EncryptedCharField()
- 查询时,
preferences__theme='dark'走 MySQL JSON 查询,encrypted_phone__startswith='138'走加密字段的模糊匹配(需在get_prep_value中兼容) - 避免把
encrypted_phone塞进preferences里——那等于主动放弃索引、范围查询和 admin 编辑能力 - 已有 JSON 字段含敏感数据?必须停服,写迁移脚本把值抽出来、加密、存新字段,再删旧键 —— 不要 inline 替换
最常被忽略的一点:加密字段的 db_index=True 没用,因为密文无序;真要按手机号查,得配合数据库函数(如 MySQL 的 HEX(AES_ENCRYPT(...)))或应用层缓存映射,ORM 层做不到高效等值索引。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










