You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Cerberus自定义转换器实现MongoDB敏感数据加解密持久化?

方案完全可行!手把手教你实现

你的思路非常对——Cerberus的自定义转换器确实是处理MongoDB敏感字段加密/解密的理想方案,完全能满足你「仅加密指定字段、其余内容保持原样」的需求。下面我一步步给你讲清楚怎么实现,包括参数传递的细节:

1. 先实现基础的加密解密工具函数

首先得有靠谱的加密逻辑,这里用cryptography库的Fernet对称加密(实际项目里密钥一定要存在安全的地方,比如环境变量,别硬编码):

from cryptography.fernet import Fernet
import os

# 生成密钥(首次运行后把密钥存到环境变量,下次直接读取)
if not os.getenv("ENCRYPTION_KEY"):
    os.environ["ENCRYPTION_KEY"] = Fernet.generate_key().decode()

cipher = Fernet(os.getenv("ENCRYPTION_KEY").encode())

def encrypt_value(value):
    # 只对字符串类型的敏感值加密,非字符串直接返回
    if isinstance(value, str):
        return cipher.encrypt(value.encode()).decode()
    return value

def decrypt_value(value):
    # 解密时处理可能的异常(比如字段未加密)
    if isinstance(value, str):
        try:
            return cipher.decrypt(value.encode()).decode()
        except (Fernet.InvalidToken, TypeError):
            return value
    return value

2. 自定义Cerberus转换器类

Cerberus的转换器方法需要遵循它的规范,我们可以把encrypt参数通过字段的Schema配置传递进去,不用给方法加默认值:

from cerberus import BaseNormalizer

class EncryptionNormalizer(BaseNormalizer):
    def _normalize_coerce_encrypt_decrypt(self, value):
        # 从当前字段的Schema配置中读取encrypt参数
        encrypt = self.schema.get("encrypt", True)
        
        # 只做加密/解密,不添加任何额外标准化操作
        if encrypt:
            return encrypt_value(value)
        else:
            return decrypt_value(value)

3. 在Eve中注册转换器并配置Schema

接下来把自定义转换器注册到Eve,然后在需要加密的字段上指定使用这个转换器,并设置encrypt参数:

from eve import Eve

# 定义你的数据模型Schema
DOMAIN = {
    "users": {
        "schema": {
            "username": {"type": "string"},
            # 针对敏感字段配置转换器和加密参数
            "pseudo_password": {
                "type": "string",
                "normalizer": "encrypt_decrypt",  # 指定用我们的自定义转换器
                "encrypt": True  # 存储时开启加密
            },
            "other_field": {"type": "string"}  # 这个字段不会被处理,保持原样
        }
    }
}

# 注册自定义转换器
NORMALIZERS = [EncryptionNormalizer()]

# 初始化Eve app
app = Eve(domain=DOMAIN)

4. 处理读取时的解密逻辑

因为Cerberus的转换器主要处理输入数据(也就是客户端POST/PUT过来的内容),所以存储时已经自动加密了。但读取数据时需要解密返回给客户端,我们可以用Eve的钩子函数来实现:

def decrypt_sensitive_fields(resource, item):
    # 只处理指定资源的敏感字段
    if resource == "users" and "pseudo_password" in item:
        item["pseudo_password"] = decrypt_value(item["pseudo_password"])

# 绑定钩子:处理单条数据和批量数据的读取
app.on_fetched_item += decrypt_sensitive_fields
app.on_fetched_resource += lambda r, items: [decrypt_sensitive_fields(r, item) for item in items]

if __name__ == "__main__":
    app.run()

关于参数传递的说明

你不用手动调用转换器,Eve会根据Schema配置自动触发:

  • 当客户端提交数据(POST/PUT)时,Eve会对pseudo_password字段调用encrypt_decrypt转换器,读取Schema里的encrypt=True参数,自动加密后存入MongoDB;
  • 当读取数据时,钩子函数会自动调用解密函数,把加密后的字段还原成明文返回给客户端。

这个方案完全符合你的要求:只加密指定字段,其余文档内容不受任何影响,也没有额外的标准化操作。

内容的提问来源于stack exchange,提问作者Mayday

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 11:57:35