first commit

This commit is contained in:
李寻欢 2022-08-26 10:35:14 +08:00
commit 5f650fe975
8 changed files with 257 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea
vendor
logs
cache
log
config.yaml

77
core.go Normal file
View File

@ -0,0 +1,77 @@
package sensitive
import (
"reflect"
"strings"
)
// Desensitization
// @description: 脱敏
// @param r any: 要脱敏的数据(只支持结构体)
// @param skip bool: 是否跳过脱敏
// @return err error: 错误信息
func Desensitization(r any, skip bool) (err error) {
// 处理Data的值如果也是结构体就处理每个字段的标签如果包含sen就把sen改为sen_id
if r == nil || skip {
return
}
// 判断是不是结构体
isPointer := reflect.TypeOf(r).Kind() == reflect.Ptr
dataType := reflect.TypeOf(r).Kind()
if isPointer {
dataType = reflect.TypeOf(r).Elem().Kind()
}
//log.Printf("数据类型: %v是否为指针: %v", dataType, isPointer)
// 处理是数组的情况
if dataType == reflect.Slice || dataType == reflect.Array {
//log.Println("传入类型为数组")
rs := reflect.ValueOf(r)
if isPointer {
rs = rs.Elem()
}
for i := 0; i < rs.Len(); i++ {
id := rs.Index(i)
err = Desensitization(id.Addr().Interface(), skip)
if err != nil {
return
}
}
}
// 如果是指针结构体,处理脱敏
if dataType == reflect.Struct {
val := reflect.ValueOf(r)
if isPointer {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
f := val.Field(i)
tag := val.Type().Field(i).Tag.Get("sen")
//log.Printf("类型: %v -> %v 值: %v 脱敏标签是否存在: %v", f.Type(), f.Kind(), f.Interface(), tag != "")
// 如果是结构体,递归调用
if f.Kind() == reflect.Interface {
//log.Println("开始处理子级")
err = Desensitization(f.Interface(), skip)
}
if tag != "" {
// 脱敏标签存在处理一下取出规则Id和占位符
ruleId := strings.Split(tag, ",")[0]
placeholder := strings.Split(tag, ",")[1]
//log.Printf("脱敏规则Id: %v, 占位符: %v", ruleId, placeholder)
// 处理脱敏
if handle, ok := senRuleMap[ruleId]; ok {
newData := handle(f.Interface().(string), placeholder)
//log.Printf("脱敏后的值: %v", newData)
f.SetString(newData)
}
}
}
}
return
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/lixh00/sensitive
go 1.18

17
handle/id_card.go Normal file
View File

@ -0,0 +1,17 @@
package handle
import (
"strings"
"unicode/utf8"
)
// IdCard
// @description: 脱敏规则: 身份证号码
// @param src string: 待处理字符串
// @param placeholder string: 占位符
// @return dst string: 脱敏后的数据
func IdCard(src, placeholder string) (dst string) {
// 保留前六位后两位
dst = src[:6] + strings.Repeat(placeholder, utf8.RuneCountInString(src)-7) + src[len(src)-2:]
return
}

21
handle/phone.go Normal file
View File

@ -0,0 +1,21 @@
package handle
import (
"strings"
"unicode/utf8"
)
// Phone
// @description: 脱敏规则: 手机号
// @param src string: 待处理字符串
// @param placeholder string: 占位符
// @return dst string: 脱敏后的数据
func Phone(src, placeholder string) (dst string) {
// 不足7位直接返回
if utf8.RuneCountInString(src) <= 7 {
return src
}
// 取前三位和后四位
dst = src[:3] + strings.Repeat(placeholder, utf8.RuneCountInString(src)-7) + src[len(src)-4:]
return
}

32
readme.md Normal file
View File

@ -0,0 +1,32 @@
## 脱敏
### 作用
利用反射处理结构体,处理带`sen`标签的字段(规则: `规则名称,占位符`,如:`phone,*`),将其脱敏。支持自定义处理函数
### 定义结构体示例
```go
package main
import (
"github.com/lixh00/sensitive"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone" sen:"phone,*"`
}
data := User{
Name: "lixh",
Age: 18,
Phone: "13888888888",
}
if err := sensitive.Desensitize(data); err != nil {
fmt.Println(err)
}
bs, _ := json.Marshal(response)
log.Printf("脱敏后的数据: %v", string(bs))
```

23
rule.go Normal file
View File

@ -0,0 +1,23 @@
package sensitive
import "sensitive/handle"
// RuleHandler 脱敏规则处理接口
type RuleHandler func(src, placeholder string) (dst string)
var senRuleMap map[string]RuleHandler // 脱敏规则Map
// 初始化函数,初始化默认的脱敏规则
func init() {
senRuleMap = make(map[string]RuleHandler)
AddHandler("phone", handle.Phone)
AddHandler("idCard", handle.IdCard)
}
// AddHandler
// @description: 添加脱敏规则处理函数
// @param name string: 脱敏规则名称
// @param handler SensitiveRuleHandler: 脱敏规则处理函数
func AddHandler(name string, handler RuleHandler) {
senRuleMap[name] = handler
}

78
run_test.go Normal file
View File

@ -0,0 +1,78 @@
package sensitive
import (
"encoding/json"
"log"
"net/http"
"testing"
)
// Response 返回值
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
// PageData 分页数据通用结构体
type PageData struct {
Current int `json:"current"` // 当前页码
Size int `json:"size"` // 每页数量
Total int64 `json:"total"` // 总数
TotalPage int `json:"total_page"` // 总页数
Records any `json:"records"` // 返回数据
}
// Data 模拟返回数据结构体
type Data struct {
Id string `json:"id"`
Name string `json:"name"`
Phone string `json:"phone" sen:"phone,*"` // 使用脱敏标签,规则为手机号,替换占位字符为*
IdNumber string `json:"idNumber" sen:"idNumber,#"` // 使用脱敏标签,规则为身份证号码,替换占位字符为*
}
// 模拟测试一下
func TestDeal(t *testing.T) {
data := []Data{{
Id: "123",
Name: "张三",
Phone: "13800138000",
IdNumber: "420102199010101010",
}, {
Id: "234",
Name: "李四",
Phone: "13800138001",
IdNumber: "420102199010101011",
}}
pageData := PageData{
Current: 1,
Size: 10,
Total: 2,
TotalPage: 1,
Records: &data,
}
response := Response{
Code: http.StatusOK,
Msg: "success",
Data: pageData,
}
bs, _ := json.Marshal(response)
log.Printf("脱敏前的数据: %v", string(bs))
//if err := response.Desensitization(true); err != nil {
// log.Println(err)
//}
//bs, _ = json.Marshal(response)
//log.Printf("假设是管理员,需要跳过处理,脱敏后的数据: %v", string(bs))
if err := Desensitization(response, false); err != nil {
log.Println(err)
}
bs, _ = json.Marshal(response)
log.Printf("脱敏后的数据: %v", string(bs))
}