RPC字段自动脱敏需在序列化前干预:net/rpc通过实现json.Marshaler接口动态过滤,gRPC须在服务逻辑返回前修改响应结构体字段值,中间件无法安全实现字段级脱敏。

RPC 返回结构体字段怎么自动脱敏
Go 的 RPC(比如 net/rpc 或 gRPC)本身不提供字段级脱敏能力,所有字段默认全量序列化。想“自动”脱敏,必须在序列化前做干预——不是靠注解或中间件拦截,而是控制结构体的 MarshalJSON 行为或用中间层包装。
- 最直接的方式:给目标结构体实现
json.Marshaler接口,在MarshalJSON里手动过滤/替换敏感字段(如password、idCard) - 别依赖
json:",omitempty"或json:"-":前者只对零值生效,后者会彻底删字段但无法动态判断;脱敏需要逻辑判断,比如 “管理员可见,普通用户不可见” - 如果用的是 gRPC + protobuf,
proto文件本身不支持运行时脱敏,得在服务端构造响应前就处理好字段值(例如把User.IdCard设为空字符串或掩码)
gRPC 中间件能不能做字段级脱敏
不能。gRPC 的 unary 或 stream 中间件(如 grpc.UnaryServerInterceptor)拿到的是已序列化的 []byte 或原始 interface{},此时字段已编码,再解析再改再重序列化,开销大、易出错、还可能破坏二进制兼容性。
- 真正可行的位置只有两个:服务逻辑返回前(修改 resp 结构体),或客户端收到后(但那不属于服务端脱敏范畴)
- 有人试图在中间件里用
proto.Message反射遍历字段并置空,但反射性能差,且无法表达“按角色脱敏”这类业务逻辑 - 如果你用的是
grpc-gateway暴露 HTTP 接口,倒可以在 HTTP 层用 JSON 中间件做,但那是 API 网关层的事,和 RPC 协议本身无关
用 struct tag 控制 JSON 输出但保留 RPC 传输字段
可以共存,但要注意 net/rpc 和 gRPC 对 tag 的处理完全不同:net/rpc 用 json tag 序列化,gRPC 默认不用任何 tag(走 protobuf 编码)。所以别指望一个 tag 同时管两边。
- 对
net/rpc:老老实实用json:"name,omitempty",配合自定义MarshalJSON实现动态脱敏 - 对 gRPC:protobuf 字段名和 Go struct 字段名是映射关系,脱敏必须在赋值阶段做,比如
resp.User = &pb.User{IdCard: maskIDCard(u.IdCard)} - 别给 struct 加
xml、yaml等无关 tag,除非你真要用;多 tag 不冲突,但没意义还增加维护成本
脱敏逻辑写在哪一层最容易维护
写在 handler 或 usecase 层,而不是 model 层。结构体本身不该知道“谁能看到什么”,脱敏是策略,不是数据本质。
立即学习“go语言免费学习笔记(深入)”;
- 错误做法:在
Userstruct 里写func (u *User) MarshalJSON()...,这会让 User 承担权限逻辑,复用性极差 - 推荐做法:定义一个
func ToPublicUser(u *User, role string) *PublicUser,返回全新结构体,字段明确、无副作用 - 如果字段多、组合多(如“仅手机号后四位”、“邮箱用户名掩码”),建议封装
masker工具包,用函数选项模式配置规则,避免 if-else 堆砌










