1
0
forked from baron/baron-sso

세션 IP 표시와 로그아웃 처리 보강

This commit is contained in:
2026-04-06 13:25:36 +09:00
parent 6a3bb19e7d
commit 2ca26cafb2
11 changed files with 292 additions and 63 deletions

View File

@@ -0,0 +1,87 @@
package utils
import (
"net"
"strings"
)
// ResolveClientIP selects the best client IP from proxy headers and the remote address.
// It prefers a public IP from X-Forwarded-For, then X-Real-IP, and finally the remote IP.
func ResolveClientIP(forwardedFor, realIP, remoteIP string) string {
forwardedCandidates := splitClientIPs(forwardedFor)
if ip := firstPublicIP(forwardedCandidates); ip != "" {
return ip
}
if ip := normalizeIP(realIP); ip != "" && !IsPrivateOrReservedIP(ip) {
return ip
}
if ip := normalizeIP(remoteIP); ip != "" && !IsPrivateOrReservedIP(ip) {
return ip
}
if len(forwardedCandidates) > 0 {
return forwardedCandidates[0]
}
if ip := normalizeIP(realIP); ip != "" {
return ip
}
return normalizeIP(remoteIP)
}
// IsPrivateOrReservedIP reports whether the IP is private or from a non-public network range.
func IsPrivateOrReservedIP(raw string) bool {
ip := net.ParseIP(strings.TrimSpace(raw))
if ip == nil {
return false
}
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() {
return true
}
for _, cidr := range []string{
"100.64.0.0/10",
"fc00::/7",
} {
_, network, err := net.ParseCIDR(cidr)
if err == nil && network.Contains(ip) {
return true
}
}
return false
}
func splitClientIPs(forwardedFor string) []string {
if strings.TrimSpace(forwardedFor) == "" {
return nil
}
parts := strings.Split(forwardedFor, ",")
result := make([]string, 0, len(parts))
for _, part := range parts {
if ip := normalizeIP(part); ip != "" {
result = append(result, ip)
}
}
return result
}
func firstPublicIP(candidates []string) string {
for _, candidate := range candidates {
if !IsPrivateOrReservedIP(candidate) {
return candidate
}
}
return ""
}
func normalizeIP(raw string) string {
raw = strings.TrimSpace(raw)
if raw == "" {
return ""
}
if host, _, err := net.SplitHostPort(raw); err == nil {
raw = host
}
ip := net.ParseIP(raw)
if ip == nil {
return ""
}
return ip.String()
}

View File

@@ -0,0 +1,24 @@
package utils
import "testing"
func TestResolveClientIP_PrefersPublicForwardedIP(t *testing.T) {
got := ResolveClientIP("100.100.100.1, 203.0.113.25, 10.0.0.2", "", "172.18.0.5")
if got != "203.0.113.25" {
t.Fatalf("expected public forwarded IP, got %q", got)
}
}
func TestResolveClientIP_FallsBackToFirstForwardedWhenAllPrivate(t *testing.T) {
got := ResolveClientIP("100.100.100.1, 10.0.0.2", "192.168.0.10", "172.18.0.5")
if got != "100.100.100.1" {
t.Fatalf("expected first forwarded private IP, got %q", got)
}
}
func TestResolveClientIP_PrefersPublicRealIPOverPrivateForwarded(t *testing.T) {
got := ResolveClientIP("100.100.100.1, 10.0.0.2", "198.51.100.7", "172.18.0.5")
if got != "198.51.100.7" {
t.Fatalf("expected public real IP, got %q", got)
}
}