From 270bf3b6bb3b450c9b24d7791b8460eae640c3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Mon, 5 Jan 2026 15:08:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=99=90=E6=B5=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/rate_limiter.go | 190 ++----------------------------------- 1 file changed, 8 insertions(+), 182 deletions(-) diff --git a/middleware/rate_limiter.go b/middleware/rate_limiter.go index ba0e745..70dc61e 100644 --- a/middleware/rate_limiter.go +++ b/middleware/rate_limiter.go @@ -8,7 +8,6 @@ import ( "gitee.com/red-future---jilin-g/common/utils" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" ) @@ -63,46 +62,23 @@ func IPLimiter(r *ghttp.Request) { // UserLimiter 用户维度限流中间件(防止单用户滥用) func UserLimiter(r *ghttp.Request) { - // 从JWT获取用户ID(如果已登录) - var userId string - var isAuth bool = false - - if token := r.Header.Get("Authorization"); token != "" && gstr.HasPrefix(token, "Bearer ") { - // 这里应该解析JWT获取用户ID,简化示例中直接使用token - tokenStr := gstr.SubStrFrom(token, "7") - if tokenStr != "" && validateToken(tokenStr) { - userId = tokenStr - isAuth = true - } + var userName string + user, err := utils.GetUserInfo(r.GetCtx()) + if err != nil { + r.Response.WriteStatusExit(429, err.Error()) + return } - - // 如果没有userId,使用IP作为标识 - if userId == "" { - userId = "anon:" + r.GetClientIp() - } - + userName = gconv.String(user.UserName) // 从配置文件读取用户限流参数 - var userLimit int64 - if isAuth { - userLimit = g.Cfg().MustGet(r.GetCtx(), "rate.user.authenticated.limit", 50).Int64() - } else { - userLimit = g.Cfg().MustGet(r.GetCtx(), "rate.user.anonymous.limit", 20).Int64() - } - - key := fmt.Sprintf(redis.RateLimitKeyUser, userId) + userLimit := g.Cfg().MustGet(r.GetCtx(), "rate.user.limit", 50).Int64() + key := fmt.Sprintf(redis.RateLimitKeyUser, userName) count, err := redis.IncrRateLimit(r.GetCtx(), key, 1) if err != nil { g.Log().Errorf(r.GetCtx(), "用户限流Redis错误: %v", err) - r.Middleware.Next() return } if count > userLimit { - userType := "已登录" - if !isAuth { - userType = "未登录" - } - g.Log().Warningf(r.GetCtx(), "用户限流触发: %s, count: %d, limit: %d, type: %s", userId, count, userLimit, userType) r.Response.WriteStatusExit(429, "您的请求过于频繁,请稍后再试") return } @@ -147,153 +123,3 @@ func ServiceLimiter(r *ghttp.Request) { r.Middleware.Next() } - -// OrderCreateLimiter 订单创建限流中间件 -// 限制: 每个用户每分钟最多创建10个订单 -func OrderCreateLimiter(r *ghttp.Request) { - userId := getUserIdFromContext(r) // 从context获取用户ID - if userId == "" { - // 如果无法获取用户信息,跳过限流检查 - r.Middleware.Next() - return - } - - key := fmt.Sprintf(redis.RateLimitKeyOrder, userId) - - // 限制: 每个用户每分钟最多创建10个订单 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 60) // 60秒窗口 - if err != nil { - g.Log().Errorf(r.GetCtx(), "订单创建限流Redis错误: %v", err) - r.Middleware.Next() - return - } - - if count > 10 { - g.Log().Warningf(r.GetCtx(), "订单创建限流触发: %s, count: %d", userId, count) - r.Response.WriteJsonExit(ghttp.DefaultHandlerResponse{ - Code: 429, - Message: "下单过于频繁,请稍后再试", - }) - return - } - - r.Middleware.Next() -} - -// WalletTransferLimiter 钱包转账限流中间件 -// 限制: 每个用户每分钟最多转账5次 -func WalletTransferLimiter(r *ghttp.Request) { - userId := getUserIdFromContext(r) // 从context获取用户ID - if userId == "" { - r.Middleware.Next() - return - } - - key := fmt.Sprintf(redis.RateLimitKeyTransfer, userId) - - // 限制: 每个用户每分钟最多转账5次 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 60) // 60秒窗口 - if err != nil { - g.Log().Errorf(r.GetCtx(), "钱包转账限流Redis错误: %v", err) - r.Middleware.Next() - return - } - - if count > 5 { - g.Log().Warningf(r.GetCtx(), "钱包转账限流触发: %s, count: %d", userId, count) - r.Response.WriteJsonExit(ghttp.DefaultHandlerResponse{ - Code: 429, - Message: "转账操作过于频繁,请稍后再试", - }) - return - } - - r.Middleware.Next() -} - -// CSMessageLimiter 客服消息限流中间件 -// 限制: 每个用户每分钟最多发送30条消息 -func CSMessageLimiter(r *ghttp.Request) { - userId := getUserIdFromContext(r) // 从context获取用户ID - if userId == "" { - r.Middleware.Next() - return - } - - key := fmt.Sprintf(redis.RateLimitKeyMessage, userId) - - // 限制: 每个用户每分钟最多发送30条消息 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 60) // 60秒窗口 - if err != nil { - g.Log().Errorf(r.GetCtx(), "客服消息限流Redis错误: %v", err) - r.Middleware.Next() - return - } - - if count > 30 { - g.Log().Warningf(r.GetCtx(), "客服消息限流触发: %s, count: %d", userId, count) - r.Response.WriteJsonExit(ghttp.DefaultHandlerResponse{ - Code: 429, - Message: "消息发送过于频繁,请稍后再试", - }) - return - } - - r.Middleware.Next() -} - -// OSSUploadLimiter 文件上传限流中间件 -// 限制: 每个用户每分钟最多上传10个文件 -func OSSUploadLimiter(r *ghttp.Request) { - userId := getUserIdFromContext(r) // 从context获取用户ID - if userId == "" { - r.Middleware.Next() - return - } - - key := fmt.Sprintf(redis.RateLimitKeyUpload, userId) - - // 限制: 每个用户每分钟最多上传10个文件 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 60) // 60秒窗口 - if err != nil { - g.Log().Errorf(r.GetCtx(), "文件上传限流Redis错误: %v", err) - r.Middleware.Next() - return - } - - if count > 10 { - g.Log().Warningf(r.GetCtx(), "文件上传限流触发: %s, count: %d", userId, count) - r.Response.WriteJsonExit(ghttp.DefaultHandlerResponse{ - Code: 429, - Message: "文件上传过于频繁,请稍后再试", - }) - return - } - - r.Middleware.Next() -} - -// getUserIdFromContext 从请求上下文中获取用户ID -// 使用项目中已有的utils.GetUserInfo方法 -func getUserIdFromContext(r *ghttp.Request) string { - // 使用项目中已有的utils.GetUserInfo方法获取用户信息 - user, err := utils.GetUserInfo(r.GetCtx()) - if err != nil { - // 如果获取用户信息失败,返回空字符串 - return "" - } - - // 在这个项目中,UserName就是用来标识用户的ID - // 转换为字符串类型 - if user.UserName != nil { - return gconv.String(user.UserName) - } - - return "" -} - -// validateToken 验证token有效性 -func validateToken(token string) bool { - // 实现 token 验证逻辑 - return token == "valid-token" -}