pii_stream.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package helper
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "strconv"
  7. )
  8. // MaskJSONBytes 对已序列化的 JSON 字节流做流式脱敏,返回脱敏后的 JSON 字节。
  9. //
  10. // 实现要点(优化 B):
  11. // - 使用 json.Decoder 的 token 流式解析,不构建完整的 interface{} 对象树,
  12. // 内存占用为常数级(栈深度 = JSON 嵌套深度)。
  13. // - 单次遍历:读到 object 的 key 命中敏感规则时,对其对应的 value 做脱敏;
  14. // 其余 token 原样透传。
  15. // - 若敏感 key 的 value 是对象或数组,则整个子树被完全脱敏(fail-safe)。
  16. // - 解析失败时返回 error(fail-closed,由上层拒绝输出,不泄露原始数据)。
  17. func MaskJSONBytes(in []byte) ([]byte, error) {
  18. dec := json.NewDecoder(bytes.NewReader(in))
  19. dec.UseNumber()
  20. var out bytes.Buffer
  21. w := &jsonWriter{buf: &out}
  22. if err := maskValue(dec, w, nil); err != nil {
  23. return nil, fmt.Errorf("PII 脱敏失败: %w", err)
  24. }
  25. // 确认没有多余内容(例如尾部垃圾),保证输入是单个合法 JSON 值。
  26. if dec.More() {
  27. return nil, fmt.Errorf("PII 脱敏失败: JSON 存在多余的尾部内容")
  28. }
  29. return out.Bytes(), nil
  30. }
  31. // maskValue 读取下一个 JSON 值并写入 w。
  32. // forceMask 非 nil 时,表示当前值处于某个敏感 key 之下,整个值(含子树)需完全脱敏。
  33. func maskValue(dec *json.Decoder, w *jsonWriter, forceMask *MaskKind) error {
  34. tok, err := dec.Token()
  35. if err != nil {
  36. return err
  37. }
  38. switch t := tok.(type) {
  39. case json.Delim:
  40. switch t {
  41. case '{':
  42. return maskObject(dec, w, forceMask)
  43. case '[':
  44. return maskArray(dec, w, forceMask)
  45. default:
  46. return fmt.Errorf("非预期的分隔符: %v", t)
  47. }
  48. default:
  49. // 标量值(string / json.Number / bool / nil)
  50. return writeScalar(w, tok, forceMask)
  51. }
  52. }
  53. // maskObject 处理一个 JSON 对象。forceMask 非 nil 时整个对象子树被脱敏。
  54. func maskObject(dec *json.Decoder, w *jsonWriter, forceMask *MaskKind) error {
  55. w.openObject()
  56. for dec.More() {
  57. // 读 key
  58. keyTok, err := dec.Token()
  59. if err != nil {
  60. return err
  61. }
  62. key, ok := keyTok.(string)
  63. if !ok {
  64. return fmt.Errorf("对象 key 非字符串: %T", keyTok)
  65. }
  66. w.writeKey(key)
  67. // 决定该字段 value 的脱敏方式:
  68. // - 若已处于强制脱敏子树中,继续沿用父级 forceMask。
  69. // - 否则查规则表。
  70. var childForce *MaskKind
  71. if forceMask != nil {
  72. childForce = forceMask
  73. } else if kind, hit := maskKindFor(key); hit {
  74. k := kind
  75. childForce = &k
  76. }
  77. if err := maskValue(dec, w, childForce); err != nil {
  78. return err
  79. }
  80. }
  81. // 消费结束的 '}'
  82. if _, err := dec.Token(); err != nil {
  83. return err
  84. }
  85. w.closeObject()
  86. return nil
  87. }
  88. // maskArray 处理一个 JSON 数组。forceMask 非 nil 时数组内每个元素都被脱敏。
  89. func maskArray(dec *json.Decoder, w *jsonWriter, forceMask *MaskKind) error {
  90. w.openArray()
  91. for dec.More() {
  92. if err := maskValue(dec, w, forceMask); err != nil {
  93. return err
  94. }
  95. }
  96. // 消费结束的 ']'
  97. if _, err := dec.Token(); err != nil {
  98. return err
  99. }
  100. w.closeArray()
  101. return nil
  102. }
  103. // writeScalar 写入一个标量值,必要时脱敏。
  104. func writeScalar(w *jsonWriter, tok json.Token, forceMask *MaskKind) error {
  105. if forceMask != nil {
  106. // 处于敏感字段之下:
  107. // - 字符串按具体 MaskKind 处理
  108. // - 其余标量(数字/bool,例如经纬度以 number 存储)统一替换为掩码字符串
  109. if s, ok := tok.(string); ok {
  110. return w.writeValueRaw(quote(applyMaskString(*forceMask, s)))
  111. }
  112. if tok == nil {
  113. // null 保持 null(无信息泄露)
  114. return w.writeValueRaw("null")
  115. }
  116. return w.writeValueRaw(quote(maskFull()))
  117. }
  118. // 非敏感字段:原样写出
  119. switch v := tok.(type) {
  120. case string:
  121. return w.writeValueRaw(quote(v))
  122. case json.Number:
  123. return w.writeValueRaw(v.String())
  124. case bool:
  125. return w.writeValueRaw(strconv.FormatBool(v))
  126. case nil:
  127. return w.writeValueRaw("null")
  128. default:
  129. return fmt.Errorf("非预期的标量类型: %T", tok)
  130. }
  131. }
  132. // quote 将字符串按 JSON 规则转义并加引号。
  133. func quote(s string) string {
  134. b, _ := json.Marshal(s)
  135. return string(b)
  136. }
  137. // indentJSON 将紧凑 JSON 重新缩进为两空格格式,便于阅读并与优化 A 的输出保持一致。
  138. // 缩进失败(理论上不会发生,因输入已是合法 JSON)时返回原始字节。
  139. func indentJSON(b []byte) []byte {
  140. var out bytes.Buffer
  141. if err := json.Indent(&out, b, "", " "); err != nil {
  142. return b
  143. }
  144. return out.Bytes()
  145. }
  146. // jsonWriter 负责按结构写出 JSON,管理逗号与 key/value 状态。
  147. type jsonWriter struct {
  148. buf *bytes.Buffer
  149. stack []*frame
  150. }
  151. type frame struct {
  152. isObject bool
  153. needComma bool // 是否需要在下一个元素/键前加逗号
  154. expectKey bool // 对象中:下一个 writeKey 之后期待 value
  155. pendingKey bool // 已写入 key,等待 value(value 前不加逗号)
  156. }
  157. func (w *jsonWriter) top() *frame {
  158. if len(w.stack) == 0 {
  159. return nil
  160. }
  161. return w.stack[len(w.stack)-1]
  162. }
  163. // beforeValue 在写入一个值(标量或容器起始)之前处理逗号。
  164. func (w *jsonWriter) beforeValue() {
  165. f := w.top()
  166. if f == nil {
  167. return
  168. }
  169. if f.isObject {
  170. // 对象中值紧跟 key,key 已处理逗号,这里不加
  171. if f.pendingKey {
  172. f.pendingKey = false
  173. return
  174. }
  175. }
  176. if f.needComma {
  177. w.buf.WriteByte(',')
  178. }
  179. f.needComma = true
  180. }
  181. func (w *jsonWriter) openObject() {
  182. w.beforeValue()
  183. w.buf.WriteByte('{')
  184. w.stack = append(w.stack, &frame{isObject: true})
  185. }
  186. func (w *jsonWriter) closeObject() {
  187. w.buf.WriteByte('}')
  188. w.stack = w.stack[:len(w.stack)-1]
  189. }
  190. func (w *jsonWriter) openArray() {
  191. w.beforeValue()
  192. w.buf.WriteByte('[')
  193. w.stack = append(w.stack, &frame{isObject: false})
  194. }
  195. func (w *jsonWriter) closeArray() {
  196. w.buf.WriteByte(']')
  197. w.stack = w.stack[:len(w.stack)-1]
  198. }
  199. func (w *jsonWriter) writeKey(key string) {
  200. f := w.top()
  201. if f != nil && f.needComma {
  202. w.buf.WriteByte(',')
  203. }
  204. w.buf.WriteString(quote(key))
  205. w.buf.WriteByte(':')
  206. if f != nil {
  207. f.needComma = true
  208. f.pendingKey = true
  209. }
  210. }
  211. func (w *jsonWriter) writeValueRaw(raw string) error {
  212. w.beforeValue()
  213. _, err := w.buf.WriteString(raw)
  214. return err
  215. }