| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- package middleware
- import (
- "bytes"
- "encoding/json"
- "io"
- "net/http"
- "net/url"
- "wails-app/internal/dao"
- "strconv"
- "strings"
- "time"
- "wails-app/internal/pkg"
- "github.com/gin-gonic/gin"
- "go.uber.org/zap"
- "wails-app/internal/global"
- "wails-app/internal/service"
- )
- var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
- var bufferSize = 1024
- // sensitiveFields 操作日志脱敏字段(不区分大小写):密码、令牌、密钥、支付敏感字段。
- var sensitiveFields = map[string]bool{
- "password": true, "old_password": true, "new_password": true,
- "confirm_password": true, "pwd": true, "pass": true,
- "token": true, "access_token": true, "refresh_token": true, "jwt": true,
- "secret": true, "private_key": true,
- }
- // maskSensitiveJSON 对 JSON 文本中的敏感字段值脱敏(非 JSON 原样返回)。
- func maskSensitiveJSON(data []byte) []byte {
- var obj interface{}
- if err := json.Unmarshal(data, &obj); err != nil {
- return data
- }
- out, err := json.Marshal(maskValue(obj))
- if err != nil {
- return data
- }
- return out
- }
- // maskValue 递归脱敏:命中敏感字段名时值替换为 "******"(空值保留)。
- func maskValue(v interface{}) interface{} {
- switch value := v.(type) {
- case map[string]interface{}:
- for key, child := range value {
- if sensitiveFields[strings.ToLower(key)] {
- if child != nil {
- value[key] = "******"
- }
- continue
- }
- value[key] = maskValue(child)
- }
- return value
- case []interface{}:
- for i := range value {
- value[i] = maskValue(value[i])
- }
- return value
- default:
- return v
- }
- }
- // maskResponse 响应体脱敏并截断(修复旧代码只对下载类响应截断、且误写 record.Body 字段的问题)。
- func maskResponse(resp string) string {
- masked := maskSensitiveJSON([]byte(resp))
- if len(masked) > bufferSize {
- return "[超出记录长度]"
- }
- return string(masked)
- }
- func OperationRecord() gin.HandlerFunc {
- return func(c *gin.Context) {
- var body []byte
- var userId int
- if c.Request.Method != http.MethodGet {
- var err error
- body, err = io.ReadAll(c.Request.Body)
- if err != nil {
- global.GVA_LOG.Error("read body from request error:", zap.Error(err))
- } else {
- c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
- }
- } else {
- query := c.Request.URL.RawQuery
- query, _ = url.QueryUnescape(query)
- split := strings.Split(query, "&")
- m := make(map[string]string)
- for _, v := range split {
- kv := strings.Split(v, "=")
- if len(kv) == 2 {
- m[kv[0]] = kv[1]
- }
- }
- body, _ = json.Marshal(&m)
- }
- claims, _ := utils.GetClaims(c)
- if claims != nil && claims.BaseClaims.ID != 0 {
- userId = int(claims.BaseClaims.ID)
- } else {
- id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
- if err != nil {
- userId = 0
- }
- userId = id
- }
- record := dao.SysOperationRecord{
- Ip: c.ClientIP(),
- Method: c.Request.Method,
- Path: c.Request.URL.Path,
- Agent: c.Request.UserAgent(),
- Body: "",
- UserID: userId,
- }
- // 上传文件时候 中间件日志进行裁断操作
- if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
- record.Body = "[文件]"
- } else {
- masked := maskSensitiveJSON(body)
- if len(masked) > bufferSize {
- record.Body = "[超出记录长度]"
- } else {
- record.Body = string(masked)
- }
- }
- writer := responseBodyWriter{
- ResponseWriter: c.Writer,
- body: &bytes.Buffer{},
- }
- c.Writer = writer
- now := time.Now()
- c.Next()
- latency := time.Since(now)
- record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
- record.Status = c.Writer.Status()
- record.Latency = latency
- record.Resp = maskResponse(writer.body.String())
- if err := operationRecordService.CreateSysOperationRecord(record); err != nil {
- global.GVA_LOG.Error("create operation record error:", zap.Error(err))
- }
- }
- }
- type responseBodyWriter struct {
- gin.ResponseWriter
- body *bytes.Buffer
- }
- func (r responseBodyWriter) Write(b []byte) (int, error) {
- r.body.Write(b)
- return r.ResponseWriter.Write(b)
- }
|