Просмотр исходного кода

feat(middleware): add email verification rate limit

creamlike1024 6 месяцев назад
Родитель
Сommit
543e7b0b6b
2 измененных файлов с 86 добавлено и 16 удалено
  1. 70 0
      middleware/email-verification-rate-limit.go
  2. 16 16
      router/api-router.go

+ 70 - 0
middleware/email-verification-rate-limit.go

@@ -0,0 +1,70 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"one-api/common"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+const (
+	EmailVerificationRateLimitMark = "EV"
+	EmailVerificationMaxRequests   = 2  // 30秒内最多2次
+	EmailVerificationDuration      = 30 // 30秒时间窗口
+)
+
+func redisEmailVerificationRateLimiter(c *gin.Context) {
+	ctx := context.Background()
+	rdb := common.RDB
+	key := "emailVerification:" + EmailVerificationRateLimitMark + ":" + c.ClientIP()
+
+	listLength, err := rdb.LLen(ctx, key).Result()
+	if err != nil {
+		fmt.Println("Redis限流检查失败:", err.Error())
+		c.Status(http.StatusInternalServerError)
+		c.Abort()
+		return
+	}
+
+	if listLength < EmailVerificationMaxRequests {
+		rdb.LPush(ctx, key, time.Now().Format(timeFormat))
+		rdb.Expire(ctx, key, time.Duration(EmailVerificationDuration)*time.Second)
+		c.Next()
+		return
+	}
+
+	c.JSON(http.StatusTooManyRequests, gin.H{
+		"success": false,
+		"message": fmt.Sprintf("发送过于频繁,请等待 %d 秒后再试", EmailVerificationDuration),
+	})
+	c.Abort()
+}
+
+func memoryEmailVerificationRateLimiter(c *gin.Context) {
+	key := EmailVerificationRateLimitMark + ":" + c.ClientIP()
+
+	if !inMemoryRateLimiter.Request(key, EmailVerificationMaxRequests, EmailVerificationDuration) {
+		c.JSON(http.StatusTooManyRequests, gin.H{
+			"success": false,
+			"message": "发送过于频繁,请稍后再试",
+		})
+		c.Abort()
+		return
+	}
+
+	c.Next()
+}
+
+func EmailVerificationRateLimit() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		if common.RedisEnabled {
+			redisEmailVerificationRateLimiter(c)
+		} else {
+			inMemoryRateLimiter.Init(common.RateLimitKeyExpirationDuration)
+			memoryEmailVerificationRateLimiter(c)
+		}
+	}
+}

+ 16 - 16
router/api-router.go

@@ -24,7 +24,7 @@ func SetApiRouter(router *gin.Engine) {
 		//apiRouter.GET("/midjourney", controller.GetMidjourney)
 		apiRouter.GET("/home_page_content", controller.GetHomePageContent)
 		apiRouter.GET("/pricing", middleware.TryUserAuth(), controller.GetPricing)
-		apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
+		apiRouter.GET("/verification", middleware.EmailVerificationRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
 		apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail)
 		apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword)
 		apiRouter.GET("/oauth/github", middleware.CriticalRateLimit(), controller.GitHubOAuth)
@@ -67,7 +67,7 @@ func SetApiRouter(router *gin.Engine) {
 				selfRoute.POST("/stripe/amount", controller.RequestStripeAmount)
 				selfRoute.POST("/aff_transfer", controller.TransferAffQuota)
 				selfRoute.PUT("/setting", controller.UpdateUserSetting)
-				
+
 				// 2FA routes
 				selfRoute.GET("/2fa/status", controller.Get2FAStatus)
 				selfRoute.POST("/2fa/setup", controller.Setup2FA)
@@ -86,7 +86,7 @@ func SetApiRouter(router *gin.Engine) {
 				adminRoute.POST("/manage", controller.ManageUser)
 				adminRoute.PUT("/", controller.UpdateUser)
 				adminRoute.DELETE("/:id", controller.DeleteUser)
-				
+
 				// Admin 2FA routes
 				adminRoute.GET("/2fa/stats", controller.Admin2FAStats)
 				adminRoute.DELETE("/:id/2fa", controller.AdminDisable2FA)
@@ -200,22 +200,22 @@ func SetApiRouter(router *gin.Engine) {
 		}
 
 		vendorRoute := apiRouter.Group("/vendors")
-        vendorRoute.Use(middleware.AdminAuth())
-        {
-            vendorRoute.GET("/", controller.GetAllVendors)
-            vendorRoute.GET("/search", controller.SearchVendors)
-            vendorRoute.GET("/:id", controller.GetVendorMeta)
-            vendorRoute.POST("/", controller.CreateVendorMeta)
-            vendorRoute.PUT("/", controller.UpdateVendorMeta)
-            vendorRoute.DELETE("/:id", controller.DeleteVendorMeta)
-        }
-
-        modelsRoute := apiRouter.Group("/models")
+		vendorRoute.Use(middleware.AdminAuth())
+		{
+			vendorRoute.GET("/", controller.GetAllVendors)
+			vendorRoute.GET("/search", controller.SearchVendors)
+			vendorRoute.GET("/:id", controller.GetVendorMeta)
+			vendorRoute.POST("/", controller.CreateVendorMeta)
+			vendorRoute.PUT("/", controller.UpdateVendorMeta)
+			vendorRoute.DELETE("/:id", controller.DeleteVendorMeta)
+		}
+
+		modelsRoute := apiRouter.Group("/models")
 		modelsRoute.Use(middleware.AdminAuth())
 		{
 			modelsRoute.GET("/missing", controller.GetMissingModels)
-            modelsRoute.GET("/", controller.GetAllModelsMeta)
-            modelsRoute.GET("/search", controller.SearchModelsMeta)
+			modelsRoute.GET("/", controller.GetAllModelsMeta)
+			modelsRoute.GET("/search", controller.SearchModelsMeta)
 			modelsRoute.GET("/:id", controller.GetModelMeta)
 			modelsRoute.POST("/", controller.CreateModelMeta)
 			modelsRoute.PUT("/", controller.UpdateModelMeta)