加密字段必须用指针接收,否则修改不生效;需通过结构体标签(如secure:"true")配合反射动态识别敏感字段;加密前须校验字段类型并跳过非string/[]byte类型;密钥须依赖注入而非硬编码;空值需提前判空避免panic。

加密字段必须用指针接收,否则修改不生效
Go 的函数参数是值传递,如果传入结构体本身,Encrypt 函数内部对字段的赋值只会作用在副本上。必须用指针接收才能真正写回原结构体。
常见错误现象:user.Name 调用 Encrypt() 后仍是明文,打印出来没变化。
- 定义方法时签名必须是
func (u *User) Encrypt() error,不是func (u User) Encrypt() - 调用时确保传的是地址:
&user或已声明为指针变量u := &User{...} - 如果结构体嵌套了其他结构体(比如
Profile *UserProfile),也要检查内层字段是否用指针访问
字段标签(struct tag)是控制加密范围的关键开关
硬编码字段名会严重耦合,改个字段就要改函数逻辑。用 json: 或自定义 tag(如 secure:"true")配合反射,才能让同一套加密逻辑适配不同结构体。
使用场景:用户表、订单表、配置结构体都含敏感字段,但字段名和数量不同。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 定义结构体时标注:
Name string `secure:"true"`,ID int `secure:"-"` - 反射遍历时用
field.Tag.Get("secure") == "true"判断是否处理 - 注意:反射无法读取未导出字段(首字母小写),所有需加密字段必须大写开头
- 性能影响:每次加密都走反射,高频场景建议缓存
reflect.Type和字段索引
加密前必须校验字段类型,避免 panic
反射获取字段值后直接调 SetString() 或 SetBytes() 会 panic —— 比如字段是 int 却当成 string 处理。
错误信息示例:panic: reflect.Set: value of type []byte is not assignable to type int
- 先用
field.Type.Kind()判断基础类型:string、[]byte、int等 - 只对
string和[]byte类型做加解密;其他类型(如time.Time)可跳过或转成字符串再处理 - 加密函数(如
aes.Encrypt)返回[]byte,存入string字段前要转成string(ciphertext) - 解密时反向操作:若原字段是
string,解密后用string(plaintext)赋值;若是[]byte,直接赋值
加密密钥不能硬编码,应从环境或接口注入
把密钥写死在代码里等于裸奔。自定义类型的方法本身不负责管理密钥,而是通过依赖注入接收。
典型错误:在 User.Encrypt 方法里写死 key := []byte("my-secret-12345678")
- 将加密逻辑抽成独立函数:
func EncryptField(value string, key []byte) ([]byte, error) - 自定义类型方法只负责调度:
u.Name, _ = EncryptField(u.Name, u.encryptionKey) - 密钥通过构造函数或 setter 注入:
u := &User{encryptionKey: os.Getenv("ENCRYPTION_KEY")} - 注意:AES 密钥长度必须是 16/24/32 字节,短于要求需 pad,长于要求需截断或哈希(如
sha256.Sum256(key).Sum(nil)[:32])
最易被忽略的是字段零值处理:空字符串 ""、nil 切片、零值整数——加密函数可能 panic 或产生无效密文。实际使用前务必加 if field.IsNil() || field.Len() == 0 判断。

















