本文转载自微信公众号「小小平头哥」,作者小小平头哥。转载本文请联系小小平头哥公众号。
创新互联建站-专业网站定制、快速模板网站建设、高性价比赤城网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式赤城网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖赤城地区。费用合理售后完善,10多年实体公司更值得信赖。
审计日志管理是我们在web系统开发中的常见的模块,虽然它有时并不属于业务模块的范畴,但对于系统整体来说却十分关键,用户的操作(尤其是关键操作)、用户的登录,我们的系统都应加以记录,以便后续溯源。
日志管理的方案可以看到很多,本文介绍的是一种基于Golang Gin框架的自定义中间件的实现方案,为大家抛砖引玉了。
个人认为有以下几个优势:
(1)中间件的方式可灵活地匹配路由组,从而灵活地指定需要记录日志的路由组;
(2)同一个路由组中通过context value 来区分接口是否需要记录操作日志;
(3)业务处理函数中可灵活配置需记录内容,不需集中处理。
本文转载自微信公众号「小小平头哥」,作者小小平头哥。转载本文请联系小小平头哥公众号。
图片
图片
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 插入log
func InsertLog(logs table.Logs) error {
}
func (User) UserLoginOut(c *ctx.Context) {
//设定记录日志标志
c.Set("need_to_log", true)
//设定操作类型
c.Set("operation_type", "用户退出登录")
//设定具体操作
c.Set("operation", "用户退出登录")
c.Success()
}
//设定路由组
UserRouter := apiV1Group.Group("users")
//为路由组应用中间件
UserRouter.Use(middleware.Logger())
1) 中间件处理函数中的备份原始请求体很重要,否则可能会出现业务代码无法获取请求参数的情况;
分享名称:100行代码实现审计日志中间件
当前地址:http://www.shufengxianlan.com/qtweb/news28/522278.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联