当前位置:首页 > 科技  > 软件

100行代码实现审计日志中间件

来源: 责编: 时间:2023-11-28 09:36:34 139观看
导读本文转载自微信公众号「小小平头哥」,作者小小平头哥。转载本文请联系小小平头哥公众号。审计日志管理是我们在web系统开发中的常见的模块,虽然它有时并不属于业务模块的范畴,但对于系统整体来说却十分关键,用户的操作(尤

本文转载自微信公众号「小小平头哥」,作者小小平头哥。转载本文请联系小小平头哥公众号。zTZ28资讯网——每日最新资讯28at.com

审计日志管理是我们在web系统开发中的常见的模块,虽然它有时并不属于业务模块的范畴,但对于系统整体来说却十分关键,用户的操作(尤其是关键操作)、用户的登录,我们的系统都应加以记录,以便后续溯源。zTZ28资讯网——每日最新资讯28at.com

日志管理的方案可以看到很多,本文介绍的是一种基于Golang Gin框架的自定义中间件的实现方案,为大家抛砖引玉了。zTZ28资讯网——每日最新资讯28at.com

个人认为有以下几个优势:zTZ28资讯网——每日最新资讯28at.com

(1)中间件的方式可灵活地匹配路由组,从而灵活地指定需要记录日志的路由组;zTZ28资讯网——每日最新资讯28at.com

(2)同一个路由组中通过context value 来区分接口是否需要记录操作日志;zTZ28资讯网——每日最新资讯28at.com

(3)业务处理函数中可灵活配置需记录内容,不需集中处理。zTZ28资讯网——每日最新资讯28at.com

本文转载自微信公众号「小小平头哥」,作者小小平头哥。转载本文请联系小小平头哥公众号。zTZ28资讯网——每日最新资讯28at.com

01整体流程

1) 中间件函数整体的流程

图片图片zTZ28资讯网——每日最新资讯28at.com

2) 业务函数流程

图片图片zTZ28资讯网——每日最新资讯28at.com

02代码实现

1) 中间件函数实现

type Response struct {  Code int `json:"code" bson:"code"`}type bodyLogWriter struct {  gin.ResponseWriter  body *bytes.Buffer}func (w bodyLogWriter) Write(b []byte) (int, error) {  w.body.Write(b)  return w.ResponseWriter.Write(b)}const (  HttpRespSuccessCode = 0)// Logger 日志记录func Logger() gin.HandlerFunc {  return func(c *gin.Context) {   //备份请求体    blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}    c.Writer = blw    //继续执行请求    c.Next()    //判断记录标志    needToLog, ok := c.Get("need_to_log")    if !ok {      log.Warn("获取是否需要记录日志失败")      return    }    if !needToLog.(bool) {      return    }    //也可以在这儿加入白名单 判断是否是不需记录的URL    /*      url := c.Request.RequestURI      if strings.Index(url, "logout") > -1 ||        strings.Index(url, "login") > -1 {        return      }    */    // 获取请求的HTTP状态码    statusCode := c.Writer.Status()    // 获取请求IP    clientIP := common.GetClientIP(c)    isSuccess := false    //若HTTP状态码为200    if c.Writer.Status() == http.StatusOK {      var resp Response      // 获取返回的数据      err := json.Unmarshal(blw.body.Bytes(), &resp)      if err != nil {        log.Warn("Logs Operation Unmarshal Error: %s", err.Error())        return      }      //判断操作是否成功 需结合业务函数的返回值结构      if resp.Code == HttpRespSuccessCode {        isSuccess = true      }    }    if statusCode != http.StatusNotFound {      SetDBLog(c, clientIP, isSuccess)    }  }}// SetDBLog 写入日志表func SetDBLog(c *gin.Context, clientIP string, status bool) {  user, ok := c.Get("user")  if !ok {    log.Warn("审计日志-获取用户名失败")  }  //日志格式化 然后入库  logInfo := table.Logs{}  //构造日志ID 可使用其他方式替代  logInfo.LogID = NewNanoid()  if user != nil {    logInfo.Username = user.(string)  }  operatorType, exist := c.Get("operation_type")  if exist {    logInfo.OperationType = operatorType.(string)  }  logInfo.IP = clientIP  operation, exist := c.Get("operation")  if exist {    logInfo.Description = operation.(string)  }  if status == true {    logInfo.Description = logInfo.Description + "成功"  } else {    logInfo.Description = logInfo.Description + "失败"  }  //日志入库  err := InsertLog(logInfo)  if err != nil {    log.Warn("InsertLog %s error, %s", logInfo.LogID, err.Error())  }}// InsertLog 插入logfunc InsertLog(logs table.Logs) error {}


zTZ28资讯网——每日最新资讯28at.com

2) 业务函数实现

func (User) UserLoginOut(c *ctx.Context) {  //设定记录日志标志  c.Set("need_to_log", true)  //设定操作类型  c.Set("operation_type", "用户退出登录")  //设定具体操作  c.Set("operation", "用户退出登录")  c.Success()}

3) 路由组应用

//设定路由组  UserRouter := apiV1Group.Group("users")  //为路由组应用中间件  UserRouter.Use(middleware.Logger())


zTZ28资讯网——每日最新资讯28at.com

03注意事项

1) 中间件处理函数中的备份原始请求体很重要,否则可能会出现业务代码无法获取请求参数的情况;zTZ28资讯网——每日最新资讯28at.com

  1. 中间件的报错不应影响原有业务逻辑。

原文链接:https://mp.weixin.qq.com/s/7HAVAAst5IyywLxdNdTQ5gzTZ28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-34658-0.html100行代码实现审计日志中间件

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Springboot扩展点之BeanDefinitionRegistryPostProcessor,你学会了吗?

下一篇: 实现Eureka服务注册和服务发现,你学会了吗?

标签:
  • 热门焦点
Top