mirror of
https://github.com/sky22333/hubproxy.git
synced 2026-08-05 03:24:57 +08:00
限流优化
This commit is contained in:
@@ -10,5 +10,5 @@ services:
|
|||||||
logging:
|
logging:
|
||||||
driver: json-file
|
driver: json-file
|
||||||
options:
|
options:
|
||||||
max-size: "1g"
|
max-size: "200m"
|
||||||
max-file: "2"
|
max-file: "3"
|
||||||
@@ -20,8 +20,7 @@ periodHours = 3.0
|
|||||||
# 白名单中的IP不受限流限制
|
# 白名单中的IP不受限流限制
|
||||||
whiteList = [
|
whiteList = [
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
"172.17.0.0/16",
|
"127.0.0.2"
|
||||||
"192.168.1.0/24"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# IP黑名单,支持单个IP或IP段
|
# IP黑名单,支持单个IP或IP段
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ func registerFrontendRoutes(router *gin.Engine, enabled bool) {
|
|||||||
func buildRouter(cfg *config.AppConfig) *gin.Engine {
|
func buildRouter(cfg *config.AppConfig) *gin.Engine {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
utils.ConfigureTrustedProxies(router)
|
||||||
|
|
||||||
router.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
router.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||||||
log.Printf("Panic 已恢复: %v", recovered)
|
log.Printf("Panic 已恢复: %v", recovered)
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ const (
|
|||||||
MaxIPCacheSize = 10000
|
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限流器结构体
|
// IPRateLimiter IP限流器结构体
|
||||||
type IPRateLimiter struct {
|
type IPRateLimiter struct {
|
||||||
ips map[string]*rateLimiterEntry
|
ips map[string]*rateLimiterEntry
|
||||||
@@ -219,34 +232,7 @@ func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip string
|
cleanIP := extractIPFromAddress(c.ClientIP())
|
||||||
|
|
||||||
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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ipLimiter, allowed := limiter.GetLimiter(cleanIP)
|
ipLimiter, allowed := limiter.GetLimiter(cleanIP)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
func TestExtractIPFromAddress(t *testing.T) {
|
func TestExtractIPFromAddress(t *testing.T) {
|
||||||
if got := extractIPFromAddress("127.0.0.1:5000"); got != "127.0.0.1" {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user