更新前后端代码
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"filestoragesystem/internal/repository"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// RateLimit 令牌桶限流中间件
|
||||
// 限额从系统设置api_rate_limit读取(次/分钟),按用户ID(已认证)或IP进行限制
|
||||
func RateLimit(settingRepo *repository.SettingRepo) gin.HandlerFunc {
|
||||
var mu sync.Mutex
|
||||
buckets := make(map[string]*rate.Limiter)
|
||||
lastClean := time.Now()
|
||||
|
||||
getLimit := func() int {
|
||||
if v, err := settingRepo.GetValue("api_rate_limit"); err == nil {
|
||||
var n int
|
||||
if _, e := fmt.Sscanf(v, "%d", &n); e == nil && n > 0 {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
// 管理员后台不限流
|
||||
if IsAdmin(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
key := utils.ClientIP(c)
|
||||
if uid := CurrentUserID(c); uid > 0 {
|
||||
key = fmt.Sprintf("user:%d", uid)
|
||||
}
|
||||
|
||||
limit := getLimit()
|
||||
mu.Lock()
|
||||
// 定期清理空闲桶
|
||||
if time.Since(lastClean) > 10*time.Minute {
|
||||
buckets = make(map[string]*rate.Limiter)
|
||||
lastClean = time.Now()
|
||||
}
|
||||
limiter, ok := buckets[key]
|
||||
if !ok {
|
||||
// 每分钟limit次:速率=limit/60每秒,突发=limit
|
||||
limiter = rate.NewLimiter(rate.Limit(float64(limit)/60.0), limit)
|
||||
buckets[key] = limiter
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
if !limiter.Allow() {
|
||||
c.Header("Retry-After", "60")
|
||||
c.AbortWithStatusJSON(http.StatusTooManyRequests, utils.Response{
|
||||
Code: 429, Message: "请求过于频繁,请稍后再试",
|
||||
})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user