package identity import ( "net/http" "net/http/httptest" "net/netip" "testing" ) func TestClientIP(t *testing.T) { // 可信代理:环回 + RFC1918(docker-compose nginx 同网段场景)。 trusted := []netip.Prefix{ netip.MustParsePrefix("127.0.0.0/8"), netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("172.16.0.0/12"), netip.MustParsePrefix("192.168.0.0/16"), } limiter := NewLoginLimiter(nil, 0, 0, trusted) cases := []struct { name string remoteAddr string xfwd string want string }{ {"xfwd first value from trusted proxy", "10.0.0.1:52341", "203.0.113.9, 10.0.0.2", "203.0.113.9"}, {"xfwd single from trusted proxy", "10.0.0.1:52341", "198.51.100.7", "198.51.100.7"}, {"xfwd with spaces from trusted proxy", "10.0.0.1:52341", " 192.0.2.5 ", "192.0.2.5"}, {"xfwd ignored from untrusted public peer", "203.0.113.9:8080", "198.51.100.7", "203.0.113.9"}, {"xfwd ignored from CGNAT peer outside trust", "100.64.0.5:8080", "198.51.100.7", "100.64.0.5"}, {"no xfwd falls back to remote", "203.0.113.9:8080", "", "203.0.113.9"}, {"no xfwd and no port", "[2001:db8::1]:443", "", "2001:db8::1"}, {"trusted peer without xfwd uses peer", "172.20.0.2:8080", "", "172.20.0.2"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", nil) req.RemoteAddr = tc.remoteAddr if tc.xfwd != "" { req.Header.Set("X-Forwarded-For", tc.xfwd) } if got := limiter.ClientIP(req); got != tc.want { t.Fatalf("ClientIP() = %q, want %q", got, tc.want) } }) } } func TestClientIPNoTrustedProxies(t *testing.T) { // 未配置可信代理时(如网关端口直接暴露):任何对端的 XFF 都被忽略。 limiter := NewLoginLimiter(nil, 0, 0, nil) req := httptest.NewRequest(http.MethodPost, "/", nil) req.RemoteAddr = "203.0.113.9:8080" req.Header.Set("X-Forwarded-For", "198.51.100.7") if got := limiter.ClientIP(req); got != "203.0.113.9" { t.Fatalf("ClientIP() = %q, want %q", got, "203.0.113.9") } }