全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-21 8 分钟 ✍️ juanwangdev

GORM 数据脱敏处理

GORM 通过 AfterFind 钩子在数据查询后自动脱敏敏感字段,确保返回业务层的隐私数据已做掩码处理。

脱敏机制概述

脱敏与加密的区别

特性脱敏加密
目的展示时隐藏部分信息存储时保护数据
可逆性不可逆可逆
场景日志、列表展示、接口返回数据库落盘存储

常见脱敏规则

手机号脱敏

Go
func MaskPhone(phone string) string {
    if len(phone) < 7 {
        return "***"
    }
    return phone[:3] + "****" + phone[len(phone)-4:]
}

// 示例: 13812345678 -> 138****5678

身份证脱敏

Go
func MaskIDCard(idCard string) string {
    if len(idCard) < 10 {
        return "***"
    }
    return idCard[:6] + "********" + idCard[len(idCard)-4:]
}

// 示例: 110101199001011234 -> 110101********1234

邮箱脱敏

Go
func MaskEmail(email string) string {
    parts := strings.Split(email, "@")
    if len(parts) != 2 {
        return "***"
    }
    local := parts[0]
    if len(local) <= 2 {
        return "**@" + parts[1]
    }
    return local[:2] + "***@" + parts[1]
}

// 示例: zhang@example.com -> zh***@example.com

姓名脱敏

Go
func MaskName(name string) string {
    runes := []rune(name)
    if len(runes) == 0 {
        return "*"
    }
    if len(runes) == 1 {
        return "*"
    }
    return string(runes[0]) + "*"
}

// 示例: 张三 -> 张*
// 示例: 欧阳修 -> 欧**

模型钩子实现

AfterFind 自动脱敏

Go
type Customer struct {
    ID       uint   `gorm:"primaryKey"`
    Name     string
    Phone    string
    IDCard   string
    Email    string
    
    // 脱敏字段(不映射数据库)
    MaskedPhone  string `gorm:"-:all"`
    MaskedIDCard string `gorm:"-:all"`
    MaskedEmail  string `gorm:"-:all"`
}

func (c *Customer) AfterFind(tx *gorm.DB) error {
    c.MaskedPhone = MaskPhone(c.Phone)
    c.MaskedIDCard = MaskIDCard(c.IDCard)
    c.MaskedEmail = MaskEmail(c.Email)
    return nil
}

// 查询时自动脱敏
var customer Customer
db.First(&customer, 1)
// customer.MaskedPhone = "138****5678"
// customer.MaskedIDCard = "110101********1234"

选择性脱敏

上下文控制脱敏开关

Go
type ContextKey string

const SkipMaskingKey ContextKey = "skip_masking"

func (c *Customer) AfterFind(tx *gorm.DB) error {
    // 检查上下文是否跳过脱敏
    if skip, ok := tx.Statement.Context.Value(SkipMaskingKey).(bool); ok && skip {
        return nil
    }
    
    c.MaskedPhone = MaskPhone(c.Phone)
    c.MaskedIDCard = MaskIDCard(c.IDCard)
    c.MaskedEmail = MaskEmail(c.Email)
    return nil
}

// 管理员查询跳过脱敏
ctx := context.WithValue(context.Background(), SkipMaskingKey, true)
db.WithContext(ctx).First(&customer, 1)

注意: 跳过脱敏需严格权限控制,仅限管理员或审计场景使用。

批量查询脱敏

列表场景自动脱敏

Go
func ListCustomers(db *gorm.DB) ([]Customer, error) {
    var customers []Customer
    err := db.Find(&customers).Error
    // AfterFind 钩子会自动对每条记录脱敏
    return customers, err
}

脱敏与序列化

JSON 输出控制

Go
type CustomerResponse struct {
    ID    uint   `json:"id"`
    Name  string `json:"name"`
    Phone string `json:"phone"` // 输出脱敏后的值
}

func ToCustomerResponse(c *Customer) CustomerResponse {
    return CustomerResponse{
        ID:    c.ID,
        Name:  c.Name,
        Phone: c.MaskedPhone, // 使用脱敏字段
    }
}

// API 返回脱敏数据
customer, _ := ListCustomers(db)
resp := ToCustomerResponse(&customer[0])
json.NewEncoder(w).Encode(resp)

注意: 不要在模型字段上直接打 json tag,应通过转换函数控制输出,避免误泄露原始数据。

要点总结

  • 使用 AfterFind 钩子在查询后自动脱敏敏感字段
  • 手机号保留前3后4,身份证保留前6后4,邮箱保留前缀前2位
  • 脱敏不可逆,仅用于展示,存储保护需使用加密
  • 可通过上下文控制跳过脱敏,适合管理员或调试场景
  • API 输出应通过转换函数使用脱敏字段,避免直接序列化原始数据

存放路径:D:\git2\jwdev\articles\GORM\专家\安全与数据保护\数据脱敏处理.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 GORM 敏感数据加密存储
下一篇 → GORM 权限与审计日志
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库