gin中敏感字段加密需在json序列化前完成,不可依赖c.json()自动处理;推荐用导出结构体+encrypt标签+自定义marshaljson实现字段级加密,或通过响应包装中间件动态拦截修改,日志中须显式调用json.marshal触发加密逻辑。

直接在 Gin 的 JSON 响应或日志中对敏感字段加密,不能靠 c.JSON() 自动完成——它只做序列化,不干预字段值。必须在数据进入 JSON 流之前,把目标字段替换成密文。
用结构体标签 + 自定义 MarshalJSON 实现字段级加密
这是最可控、侵入性最小的方式,适合已知固定字段(如 Password、IDCard、Phone)的场景。
- 结构体字段必须是导出字段(首字母大写),否则
json.Marshal无法访问 - 给需加密字段加自定义 tag,比如
json:"phone,encrypt",用于运行时识别 - 重写结构体的
MarshalJSON()方法,在序列化前遍历字段,对带encrypttag 的字符串字段调用加密函数(如 AES 或 SM4) - 注意:加密密钥不能硬编码,应从环境变量或配置中心加载;密钥变更时要考虑旧密文兼容性
示例节选:
func (u User) MarshalJSON() ([]byte, error) {
type Alias User // 防止无限递归
raw, err := json.Marshal(Alias(u))
if err != nil {
return nil, err
}
var obj map[string]interface{}
json.Unmarshal(raw, &obj)
if phone, ok := obj["phone"].(string); ok && phone != "" {
obj["phone"] = encrypt(phone, os.Getenv("AES_KEY"))
}
return json.Marshal(obj)
}
用中间件拦截响应 Body 实现动态字段加密
适用于不想改业务结构体、或需按 URL 路径/接口维度差异化加密(比如仅 /api/v1/user 返回时加密 email)的场景。
- Gin 原生不支持修改响应 Body,需用
gin.ResponseWriter包装器捕获原始输出 - 在
Write()和WriteHeader()中拦截 JSON 字节流,用json.RawMessage解析后修改指定 key 的值 - 性能有损耗:每次响应都要反序列化 → 修改 → 序列化;高并发下需压测验证
- 字段路径嵌套深时(如
data.items[0].contact.phone),建议用gjson库而非原生json.Unmarshal,避免 struct 定义膨胀
日志中 JSON 字段加密别碰 fmt.Printf 直接打结构体
很多团队在 AOP 日志里直接 log.Println(req),结果明文泄露——Go 的默认打印不走 MarshalJSON,而是反射读取字段值。
- 必须显式调用
json.Marshal或json.MarshalIndent,才能触发你写的MarshalJSON方法 - 若用第三方日志库(如
zap),需注册自定义Encoder,对特定字段类型(如EncryptedString)做特殊序列化 - FastJSON 等高性能 JSON 库可能绕过标准
MarshalJSON接口,测试时务必验证实际输出
加密算法选型别只盯 MD5/SHA
MD5 和 SHA 是哈希,不可逆,只适用于密码校验;字段加密必须可逆,否则前端无法展示。
- 传输中加密用 TLS 就够了,JSON 字段加密本质是“防内部泄露”,场景通常是日志落盘、审计系统、管理后台
- AES-128-GCM 最常用:兼顾安全性与性能,且 Go 标准库
crypto/aes+crypto/cipher开箱即用 - 国密需求选 SM4,但
github.com/tjfoc/gmsm等库需额外维护,密钥长度、IV 生成逻辑要和 Java/Python 端对齐 - 绝对不要用 Base64 当“加密”——它只是编码,毫无安全意义
真正容易被忽略的是密钥轮换机制:没有自动密钥更新能力的系统,一旦密钥泄漏就全局失守。字段加密不是加一层壳就完事,得配套密钥生命周期管理。











