ShouldBindJSON不处理加密,加密字段需在绑定后手动加解密;响应时也须先加密再JSON序列化,不可依赖Gin自动处理。

c.ShouldBindJSON 本身不加密也不解密,它只是解析请求体。要实现“指定 JSON 字段加密传输”,必须在业务层手动加解密,不能依赖 Gin 绑定函数自动处理。
Gin 的 ShouldBindJSON 和 JSON 方法都只做序列化/反序列化,不碰加密逻辑。真正需要加密的字段,得在绑定后、业务处理前解密;在响应前、调用 c.JSON 前加密。
为什么不能在 ShouldBindJSON 里直接解密字段
因为 ShouldBindJSON 底层调用的是 json.Unmarshal,它期望输入是合法 JSON 字符串。如果你传入的是加密后的 base64 或密文字符串(比如 "name": "U2FsdGVkX1+..."),而结构体字段类型是 string,那它能成功反序列化——但你拿到的是密文,不是明文,后续业务会出错。
常见错误现象:
- 前端传了加密字段,后端
ShouldBindJSON成功,但字段值是乱码或 base64 字符串,业务逻辑直接拿去用导致校验失败 - 想让结构体 tag 自动触发解密(如
json:"name,decrypt"),Gin 不支持这种扩展语法 - 试图用自定义
UnmarshalJSON方法解密,但密钥、nonce、AAD 等上下文信息无法从json.RawMessage中安全获取
推荐做法:绑定后立即解密指定字段
把加密字段定义为 string 类型,绑定完成后,用 AES-GCM 等算法单独解密。这样控制权完全在手,可灵活处理错误(如密钥不匹配、tag 验证失败)。
示例场景:用户登录接口,password 字段前端已 AES-GCM 加密传输
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"` // 接收的是密文字符串
}
<p>func loginHandler(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": "invalid json"})
return
}</p><pre class="brush:php;toolbar:false;">// 解密 password 字段(需提供 key、nonce、aad)
plainPwd, err := aesgcm.Decrypt([]byte(req.Password), key, nonce, aad)
if err != nil {
c.JSON(400, gin.H{"error": "password decrypt failed"})
return
}
// 后续用 plainPwd 做校验、查库等
// ...}
关键点:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 解密必须在
ShouldBindJSON之后,且仅对明确标记为“加密字段”的成员操作 - 不要把解密逻辑塞进结构体方法里——Gin 不会调用
UnmarshalJSON,除非你用json.RawMessage手动控制 - 密钥、nonce、AAD 必须通过安全方式传递(如 header、固定 salt + session key 衍生),不能硬编码
响应时加密指定字段再 JSON 输出
同理,不能靠 c.JSON 自动加密。必须先构造明文响应结构体,再对特定字段加密,最后序列化。
示例:返回用户信息,但 id_card 和 phone 需加密
type UserResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
IDCard string `json:"id_card"` // 明文字段,准备被加密
Phone string `json:"phone"` // 明文字段,准备被加密
}
<p>func getUser(c *gin.Context) {
user := UserResponse{
ID: 123,
Name: "张三",
IDCard: "11010119900307271X",
Phone: "13800138000",
}</p><pre class="brush:php;toolbar:false;">// 单独加密敏感字段
user.IDCard, _ = aesgcm.Encrypt([]byte(user.IDCard), key, nonce, aad)
user.Phone, _ = aesgcm.Encrypt([]byte(user.Phone), key, nonce, aad)
c.JSON(200, user) // 此时输出的是加密后的字符串}
注意:
- 加密后字段仍是
string类型,JSON 序列化无压力 - 前端需约定好哪些字段是密文,自行解密;后端不感知“该字段是否已加密”,只负责按需加解密
- 如果字段可能为空,加密前要判空,避免对
""加密产生无效密文
中间件里统一处理?风险很高
有人想写个中间件,在 c.Next() 后扫描响应 body,自动加密某些 key。这不可行:
原因:
-
c.JSON写入的是已序列化的字节流,中间件拿到的是完整 JSON 字符串,无法精准定位某个字段并替换(尤其字段嵌套、数组、key 名重复时) - 加密需要 nonce/AAD,这些没法从 JSON 字符串里提取,必须和业务逻辑强耦合
- 性能损耗大:全文本解析 + 修改 + 重序列化,远不如业务层直写
- 一旦 JSON 结构变化(比如字段改名、新增嵌套),中间件就失效或出错
真正适合中间件做的,是网关层的全量请求/响应流加解密(即 AES-GCM 加密整个 body),而不是字段级控制。字段级必须由业务代码显式决定。
最易被忽略的一点:加密字段的 nonce 不能复用。每次加密都要生成新 nonce,并随密文一起传给前端(比如拼在 base64 后面,或额外字段返回)。Gin 本身不帮你管这个。

















