first commit
This commit is contained in:
commit
5f650fe975
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.idea
|
||||||
|
vendor
|
||||||
|
logs
|
||||||
|
cache
|
||||||
|
log
|
||||||
|
config.yaml
|
77
core.go
Normal file
77
core.go
Normal 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
|
||||||
|
}
|
17
handle/id_card.go
Normal file
17
handle/id_card.go
Normal 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
21
handle/phone.go
Normal 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
32
readme.md
Normal 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
23
rule.go
Normal 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
78
run_test.go
Normal 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))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user