Go语言脱敏应基于类型内聚:为Phone、IDCard等定义专属类型并实现String()或Mask(),通过json.Marshaler自动序列化脱敏,校验后再掩码,避免通用函数和正则滥用。

Go 语言没有内置脱敏能力,但用标准库就能实现轻量、可控、可复用的脱敏模块——关键不是“封装成多大一个包”,而是把脱敏逻辑和原始数据解耦,让 Phone、IDCard、Email 这类字段自己知道怎么藏住敏感部分。
脱敏逻辑必须绑定到具体字段类型,而不是写一堆 if-else
常见错误是写一个通用函数 Mask(value string, rule string),然后在业务层传入规则字符串。这会导致规则散落、无法静态检查、易错且难测试。正确做法是为每种敏感类型定义专属结构体,把脱敏行为内聚进去。
-
type Phone string实现String()方法,返回掩码后结果(如138****1234) -
type IDCard string实现Mask()方法,只暴露前6位+后4位(110101********1234) - 所有方法不修改原始值,保持不可变语义
- 避免在结构体里存 masked 字段——脱敏是视图行为,不是数据状态
JSON 序列化时自动脱敏,别靠中间件或手动调用
业务返回 HTTP 响应时,常需要「对某些字段脱敏、其余照常」。如果靠 json.Marshal 后字符串替换,或在 handler 里挨个调 phone.Mask(),极易漏掉新增字段或嵌套结构。Go 的 json.Marshaler 接口才是正解。
- 让
Phone实现MarshalJSON() ([]byte, error),返回已脱敏的 JSON 字符串字面量 - 注意:返回值必须是合法 JSON 字符串(带双引号),所以得用
json.Marshal(fmt.Sprintf("138****%s", p[7:]))或手动拼接加转义 - 若需开关脱敏(如内部调试接口),可用包装类型
type SafePhone struct{ Raw Phone; Debug bool },在MarshalJSON中分支处理 - 别在
UnmarshalJSON做脱敏——那是解析,不是展示
正则不是万能的,身份证/手机号要按格式校验后再脱敏
直接对任意字符串做 regexp.ReplaceAllString 掩码,会把非合规数据也“伪脱敏”,掩盖真实问题。比如把 "abc123" 脱成 "ab****3",看起来像掩码成功,实则字段根本无效。
立即学习“go语言免费学习笔记(深入)”;
- 脱敏前先校验:用
regexp.MustCompile(`^1[3-9]\d{9}$`)判手机号,用verifyIDCard(string)校验身份证末位 - 校验失败时,返回原值 or panic(开发期)or 日志告警(生产期),而非强行掩码
- 正则捕获组比全局替换更安全:
re := regexp.MustCompile(`^(1[3-9]\d{2})\d{4}(\d{4})$`),再用re.ReplaceAllStringFunc(s, "$1****$2") - 注意中文字符、全角数字、空格等干扰项——输入清洗应在脱敏之前完成
真正难的不是写几个掩码函数,而是决定「谁有权看到明文」以及「脱敏是否影响下游签名/索引逻辑」。比如日志里脱敏了手机号,但审计系统又依赖原始值做关联,这时就得用上下文传递原始值,而非从脱敏后字段反推。脱敏模块越“透明”,越容易被误用。


















