限流优化

This commit is contained in:
sky22333
2026-07-13 03:10:42 +08:00
parent 7e0b82f8f0
commit 320df5def5
5 changed files with 72 additions and 33 deletions

View File

@@ -10,5 +10,5 @@ services:
logging:
driver: json-file
options:
max-size: "1g"
max-file: "2"
max-size: "200m"
max-file: "3"

View File

@@ -20,8 +20,7 @@ periodHours = 3.0
# 白名单中的IP不受限流限制
whiteList = [
"127.0.0.1",
"172.17.0.0/16",
"192.168.1.0/24"
"127.0.0.2"
]
# IP黑名单支持单个IP或IP段

View File

@@ -90,6 +90,7 @@ func registerFrontendRoutes(router *gin.Engine, enabled bool) {
func buildRouter(cfg *config.AppConfig) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
utils.ConfigureTrustedProxies(router)
router.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
log.Printf("Panic 已恢复: %v", recovered)

View File

@@ -17,6 +17,19 @@ const (
MaxIPCacheSize = 10000
)
// 可信反代
var trustedProxyCIDRs = []string{
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}
// ConfigureTrustedProxies 可信反代
func ConfigureTrustedProxies(router *gin.Engine) {
_ = router.SetTrustedProxies(trustedProxyCIDRs)
}
// IPRateLimiter IP限流器结构体
type IPRateLimiter struct {
ips map[string]*rateLimiterEntry
@@ -219,34 +232,7 @@ func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
return
}
var ip string
if forwarded := c.GetHeader("X-Forwarded-For"); forwarded != "" {
ips := strings.Split(forwarded, ",")
ip = strings.TrimSpace(ips[0])
} else if realIP := c.GetHeader("X-Real-IP"); realIP != "" {
ip = realIP
} else if remoteIP := c.GetHeader("X-Original-Forwarded-For"); remoteIP != "" {
ips := strings.Split(remoteIP, ",")
ip = strings.TrimSpace(ips[0])
} else {
ip = c.ClientIP()
}
cleanIP := extractIPFromAddress(ip)
normalizedIP := normalizeIPForRateLimit(cleanIP)
if cleanIP != normalizedIP {
fmt.Printf("请求IP: %s (提纯后: %s, 限流段: %s), X-Forwarded-For: %s, X-Real-IP: %s\n",
ip, cleanIP, normalizedIP,
c.GetHeader("X-Forwarded-For"),
c.GetHeader("X-Real-IP"))
} else {
fmt.Printf("请求IP: %s (提纯后: %s), X-Forwarded-For: %s, X-Real-IP: %s\n",
ip, cleanIP,
c.GetHeader("X-Forwarded-For"),
c.GetHeader("X-Real-IP"))
}
cleanIP := extractIPFromAddress(c.ClientIP())
ipLimiter, allowed := limiter.GetLimiter(cleanIP)

View File

@@ -1,6 +1,11 @@
package utils
import "testing"
import (
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestExtractIPFromAddress(t *testing.T) {
if got := extractIPFromAddress("127.0.0.1:5000"); got != "127.0.0.1" {
@@ -19,3 +24,51 @@ func TestNormalizeIPv6ForRateLimit(t *testing.T) {
t.Fatalf("IPv6 normalized = %q", got)
}
}
func TestClientIPIgnoresSpoofedXFFWithoutTrustedProxy(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
ConfigureTrustedProxies(router)
var got string
router.GET("/", func(c *gin.Context) {
got = c.ClientIP()
})
req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "203.0.113.50:12345"
req.Header.Set("X-Forwarded-For", "127.0.0.1")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if got != "203.0.113.50" {
t.Fatalf("ClientIP() = %q, want 203.0.113.50", got)
}
}
func TestClientIPTrustsXFFFromTrustedProxy(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
if err := router.SetTrustedProxies([]string{"127.0.0.1"}); err != nil {
t.Fatal(err)
}
var got string
router.GET("/", func(c *gin.Context) {
got = c.ClientIP()
})
req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "127.0.0.1:54321"
req.Header.Set("X-Forwarded-For", "203.0.113.50")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if got != "203.0.113.50" {
t.Fatalf("ClientIP() = %q, want 203.0.113.50", got)
}
}