feat: 敏感操作增加IP维度限流
- RateLimiter 新增 AllowSensitive 方法(1分钟5次,超限封禁5分钟) - 新增 SensitiveRateLimit Gin 中间件 - 改密(PUT /api/settings/password)增加限流保护 - 注销(POST /api/settings/delete-account)增加限流保护 - 签到(POST /api/level/checkin)增加限流保护
This commit is contained in:
24
internal/middleware/rate.go
Normal file
24
internal/middleware/rate.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SensitiveRateLimit 敏感操作(改密/注销/签到)IP 维度限流中间件。
|
||||||
|
// 1 分钟最多 5 次请求,超限后封禁 5 分钟。
|
||||||
|
func SensitiveRateLimit(rl *RateLimiter) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
result := rl.AllowSensitive(c.ClientIP())
|
||||||
|
if result.Blocked {
|
||||||
|
c.JSON(http.StatusTooManyRequests, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": result.Message,
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -32,3 +32,8 @@ func (rl *RateLimiter) AllowAccount(email string) RateLimitResult {
|
|||||||
func (rl *RateLimiter) AllowIP(ip string) RateLimitResult {
|
func (rl *RateLimiter) AllowIP(ip string) RateLimitResult {
|
||||||
return rl.try(rl.ipFailures, ip, ipWindow, ipBlockDur, ipMaxFails, false)
|
return rl.try(rl.ipFailures, ip, ipWindow, ipBlockDur, ipMaxFails, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowSensitive 敏感操作限流(改密/注销/签到),基于 IP,1 分钟最多 5 次,超限封禁 5 分钟。
|
||||||
|
func (rl *RateLimiter) AllowSensitive(ip string) RateLimitResult {
|
||||||
|
return rl.try(rl.ipFailures, "sensitive:"+ip, sensitiveWindow, sensitiveBlockDur, sensitiveMaxRequests, false)
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,11 @@ const (
|
|||||||
ipBlockDur = 1 * time.Minute
|
ipBlockDur = 1 * time.Minute
|
||||||
cleanupInterval = 2 * time.Minute
|
cleanupInterval = 2 * time.Minute
|
||||||
maxEntries = 10000
|
maxEntries = 10000
|
||||||
|
|
||||||
|
// 敏感操作限流:改密/注销/签到
|
||||||
|
sensitiveWindow = 1 * time.Minute
|
||||||
|
sensitiveMaxRequests = 5
|
||||||
|
sensitiveBlockDur = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// windowState 单个维度的限流状态
|
// windowState 单个维度的限流状态
|
||||||
|
|||||||
@ -17,8 +17,8 @@ func setupAPIRoutes(r *gin.Engine, cfg *config.Config, d *dependencies) {
|
|||||||
{
|
{
|
||||||
settingsAPI.PUT("/profile", d.settingsCtrl.UpdateProfile)
|
settingsAPI.PUT("/profile", d.settingsCtrl.UpdateProfile)
|
||||||
settingsAPI.POST("/avatar", d.settingsCtrl.UploadAvatar)
|
settingsAPI.POST("/avatar", d.settingsCtrl.UploadAvatar)
|
||||||
settingsAPI.PUT("/password", d.settingsCtrl.ChangePassword)
|
settingsAPI.PUT("/password", middleware.SensitiveRateLimit(d.rateLimiter), d.settingsCtrl.ChangePassword)
|
||||||
settingsAPI.POST("/delete-account", d.settingsCtrl.DeleteAccount)
|
settingsAPI.POST("/delete-account", middleware.SensitiveRateLimit(d.rateLimiter), d.settingsCtrl.DeleteAccount)
|
||||||
settingsAPI.GET("/audit-status", d.settingsCtrl.AuditStatus)
|
settingsAPI.GET("/audit-status", d.settingsCtrl.AuditStatus)
|
||||||
// 登录管理
|
// 登录管理
|
||||||
settingsAPI.GET("/sessions", d.settingsCtrl.ListSessions)
|
settingsAPI.GET("/sessions", d.settingsCtrl.ListSessions)
|
||||||
@ -47,7 +47,7 @@ func setupAPIRoutes(r *gin.Engine, cfg *config.Config, d *dependencies) {
|
|||||||
levelAPI.Use(d.authMdw.BannedWriteGuard())
|
levelAPI.Use(d.authMdw.BannedWriteGuard())
|
||||||
levelAPI.Use(middleware.CSRF(cfg))
|
levelAPI.Use(middleware.CSRF(cfg))
|
||||||
{
|
{
|
||||||
levelAPI.POST("/checkin", d.levelCtrl.CheckIn)
|
levelAPI.POST("/checkin", middleware.SensitiveRateLimit(d.rateLimiter), d.levelCtrl.CheckIn)
|
||||||
levelAPI.GET("/info", d.levelCtrl.GetLevel)
|
levelAPI.GET("/info", d.levelCtrl.GetLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import (
|
|||||||
type dependencies struct {
|
type dependencies struct {
|
||||||
authCtrl *controller.AuthController
|
authCtrl *controller.AuthController
|
||||||
authMdw *middleware.AuthMiddleware
|
authMdw *middleware.AuthMiddleware
|
||||||
|
rateLimiter *middleware.RateLimiter
|
||||||
maintenanceMdw *middleware.MaintenanceMiddleware
|
maintenanceMdw *middleware.MaintenanceMiddleware
|
||||||
settingsCtrl *controller.SettingsController
|
settingsCtrl *controller.SettingsController
|
||||||
messageCtrl *controller.MessageController
|
messageCtrl *controller.MessageController
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func buildDeps(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSettings, redisClient *redis.Client) *dependencies {
|
func buildDeps(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSettings, redisClient *redis.Client) *dependencies {
|
||||||
userRepo, sessionMgr, authService, _, authCtrl, avatarSvc, authMdw := buildDepsCore(db, cfg, siteSettings, redisClient)
|
userRepo, sessionMgr, authService, rateLimiter, authCtrl, avatarSvc, authMdw := buildDepsCore(db, cfg, siteSettings, redisClient)
|
||||||
|
|
||||||
auditRepo := repository.NewAuditRepo(db)
|
auditRepo := repository.NewAuditRepo(db)
|
||||||
notificationRepo := repository.NewNotificationRepo(db)
|
notificationRepo := repository.NewNotificationRepo(db)
|
||||||
@ -107,7 +107,7 @@ func buildDeps(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSetting
|
|||||||
postService.WithHomePageInvalidator(func() { homeCache.Invalidate() })
|
postService.WithHomePageInvalidator(func() { homeCache.Invalidate() })
|
||||||
|
|
||||||
return &dependencies{
|
return &dependencies{
|
||||||
authCtrl: authCtrl, authMdw: authMdw, maintenanceMdw: maintenanceMdw,
|
authCtrl: authCtrl, authMdw: authMdw, rateLimiter: rateLimiter, maintenanceMdw: maintenanceMdw,
|
||||||
settingsCtrl: settingsCtrl,
|
settingsCtrl: settingsCtrl,
|
||||||
messageCtrl: messageController, postCtrl: postCtrl, postService: postService, spaceCtrl: spaceCtrl,
|
messageCtrl: messageController, postCtrl: postCtrl, postService: postService, spaceCtrl: spaceCtrl,
|
||||||
studioCtrl: studioCtrl,
|
studioCtrl: studioCtrl,
|
||||||
|
|||||||
Reference in New Issue
Block a user