From 83cca5d41fa1d1cdd13339614816ba153beeb059 Mon Sep 17 00:00:00 2001 From: songzhibin97 <718428482@qq.com> Date: Tue, 12 Oct 2021 20:10:23 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=E9=99=90=E6=B5=81?= =?UTF-8?q?=E4=B8=AD=E9=97=B4=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/middleware/limit_ip.go | 54 +++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/server/middleware/limit_ip.go b/server/middleware/limit_ip.go index 0e18cac9..2bbdcb4f 100644 --- a/server/middleware/limit_ip.go +++ b/server/middleware/limit_ip.go @@ -3,13 +3,58 @@ package middleware import ( "context" "errors" + "net/http" + "time" + + "go.uber.org/zap" + "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" "github.com/gin-gonic/gin" - "time" ) -// ip限制 +type LimitConfig struct { + // GenerationKey 根据业务生成key 下面CheckOrMark查询生成 + GenerationKey func(c *gin.Context) string + // 检查函数,用户可修改具体逻辑,更加灵活 + CheckOrMark func(key string, Expire int) bool + // Expire key 过期时间 + Expire int + // Limit 周期时间 + Limit int +} + +func (l *LimitConfig) LimitWithTime() gin.HandlerFunc { + return func(c *gin.Context) { + if l.CheckOrMark(l.GenerationKey(c), l.Expire) { + c.Next() + } else { + c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": "操作频繁,请稍后再试"}) + c.Abort() + return + } + } +} + +// DefaultGenerationKey 默认生成key +func DefaultGenerationKey(c *gin.Context) string { + return "GVA_Limit" + c.ClientIP() +} + +func DefaultCheckOrMark(key string, expire int, limit int) bool { + // 判断是否开启redis + if global.GVA_REDIS == nil { + return true + } + if err := SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil { + global.GVA_LOG.Error("limit", zap.Error(err)) + return false + } + return true + +} + +// IPLimit ip限制 func IPLimit() gin.HandlerFunc { return func(c *gin.Context) { key := "RequestClientIPLimit===" + c.ClientIP() @@ -29,7 +74,10 @@ func IPLimit() gin.HandlerFunc { // 设置访问次数 func SetLimitWithTime(key string, limit int, expiration time.Duration) error { count, err := global.GVA_REDIS.Exists(context.Background(), key).Result() - if err != nil || count == 0 { + if err != nil { + return err + } + if count == 0 { pipe := global.GVA_REDIS.TxPipeline() pipe.Incr(context.Background(), key) pipe.Expire(context.Background(), key, expiration) From c6501c2d0a505c22c0896735f96587df3b662228 Mon Sep 17 00:00:00 2001 From: songzhibin97 <718428482@qq.com> Date: Tue, 12 Oct 2021 20:37:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96limit=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/middleware/limit_ip.go | 46 +++++++++++++++-------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/server/middleware/limit_ip.go b/server/middleware/limit_ip.go index 2bbdcb4f..f98037f3 100644 --- a/server/middleware/limit_ip.go +++ b/server/middleware/limit_ip.go @@ -17,21 +17,21 @@ type LimitConfig struct { // GenerationKey 根据业务生成key 下面CheckOrMark查询生成 GenerationKey func(c *gin.Context) string // 检查函数,用户可修改具体逻辑,更加灵活 - CheckOrMark func(key string, Expire int) bool + CheckOrMark func(key string, expire int, limit int) error // Expire key 过期时间 Expire int // Limit 周期时间 Limit int } -func (l *LimitConfig) LimitWithTime() gin.HandlerFunc { +func (l LimitConfig) LimitWithTime() gin.HandlerFunc { return func(c *gin.Context) { - if l.CheckOrMark(l.GenerationKey(c), l.Expire) { - c.Next() - } else { - c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": "操作频繁,请稍后再试"}) + if err := l.CheckOrMark(l.GenerationKey(c), l.Expire, l.Limit); err != nil { + c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err}) c.Abort() return + } else { + c.Next() } } } @@ -41,37 +41,29 @@ func DefaultGenerationKey(c *gin.Context) string { return "GVA_Limit" + c.ClientIP() } -func DefaultCheckOrMark(key string, expire int, limit int) bool { +func DefaultCheckOrMark(key string, expire int, limit int) (err error) { // 判断是否开启redis if global.GVA_REDIS == nil { - return true + return err } - if err := SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil { + if err = SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil { global.GVA_LOG.Error("limit", zap.Error(err)) - return false + } - return true + return err } -// IPLimit ip限制 -func IPLimit() gin.HandlerFunc { - return func(c *gin.Context) { - key := "RequestClientIPLimit===" + c.ClientIP() - limit := global.GVA_CONFIG.System.LimitCountIP - second := global.GVA_CONFIG.System.LimitTimeIP - expiration := time.Duration(second) * time.Second - if err := SetLimitWithTime(key, limit, expiration); err != nil { - response.FailWithMessage(err.Error(), c) - c.Abort() - return - } - // 继续往下处理 - c.Next() - } +func DefaultLimit() gin.HandlerFunc { + return LimitConfig{ + GenerationKey: DefaultGenerationKey, + CheckOrMark: DefaultCheckOrMark, + Expire: global.GVA_CONFIG.System.LimitTimeIP, + Limit: global.GVA_CONFIG.System.LimitCountIP, + }.LimitWithTime() } -// 设置访问次数 +// SetLimitWithTime 设置访问次数 func SetLimitWithTime(key string, limit int, expiration time.Duration) error { count, err := global.GVA_REDIS.Exists(context.Background(), key).Result() if err != nil { From bdfb04e3beb1e67cf229105c8835c8ad4564bd6f Mon Sep 17 00:00:00 2001 From: songzhibin97 <718428482@qq.com> Date: Wed, 13 Oct 2021 13:04:56 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=E4=B8=AD=E9=97=B4=E4=BB=B6=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=81=9A=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/middleware/limit_ip.go | 2 +- server/middleware/logger.go | 87 +++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 server/middleware/logger.go diff --git a/server/middleware/limit_ip.go b/server/middleware/limit_ip.go index f98037f3..9e951415 100644 --- a/server/middleware/limit_ip.go +++ b/server/middleware/limit_ip.go @@ -73,7 +73,7 @@ func SetLimitWithTime(key string, limit int, expiration time.Duration) error { pipe := global.GVA_REDIS.TxPipeline() pipe.Incr(context.Background(), key) pipe.Expire(context.Background(), key, expiration) - _, err := pipe.Exec(context.Background()) + _, err = pipe.Exec(context.Background()) return err } else { //次数 diff --git a/server/middleware/logger.go b/server/middleware/logger.go new file mode 100644 index 00000000..5bfda9a5 --- /dev/null +++ b/server/middleware/logger.go @@ -0,0 +1,87 @@ +package middleware + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +// LogLayout 日志layout +type LogLayout struct { + Time time.Time + Metadata map[string]interface{} // 存储自定义原数据 + Path string // 访问路径 + Query string // 携带query + Body string // 携带body数据 + IP string // ip地址 + UserAgent string // 代理 + Error string // 错误 + Cost time.Duration // 花费时间 + Source string // 来源 +} + +type Logger struct { + // Filter 用户自定义过滤 + Filter func(c *gin.Context) bool + // FilterKeyword 关键字过滤(key) + FilterKeyword func(layout *LogLayout) bool + // AuthProcess 鉴权处理 + AuthProcess func(c *gin.Context, layout *LogLayout) + // 日志处理 + Print func(LogLayout) + // Source 服务唯一标识 + Source string +} + +func (l Logger) SetLoggerMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + query := c.Request.URL.RawQuery + var body []byte + if l.Filter != nil && !l.Filter(c) { + body, _ = c.GetRawData() + // 将原body塞回去 + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + } + c.Next() + cost := time.Since(start) + layout := LogLayout{ + Time: time.Now(), + Path: path, + Query: query, + IP: c.ClientIP(), + UserAgent: c.Request.UserAgent(), + Error: strings.TrimRight(c.Errors.ByType(gin.ErrorTypePrivate).String(), "\n"), + Cost: cost, + Source: l.Source, + } + if l.Filter != nil && !l.Filter(c) { + layout.Body = string(body) + } + // 处理鉴权需要的信息 + l.AuthProcess(c, &layout) + if l.FilterKeyword != nil { + // 自行判断key/value 脱敏等 + l.FilterKeyword(&layout) + } + // 自行处理日志 + l.Print(layout) + } +} + +func DefaultLogger() gin.HandlerFunc { + return Logger{ + Print: func(layout LogLayout) { + // 标准输出,k8s做收集 + v, _ := json.Marshal(layout) + fmt.Println(string(v)) + }, + Source: "GVA", + }.SetLoggerMiddleware() +}