// Package middleware 提供请求日志、错误恢复、认证/授权与审计等 HTTP 中间件。 package middleware import ( "context" "fmt" "net" "net/http" "strings" "sync/atomic" "git.yeij.top/AskaEth/Scoreboard/server/internal/auth" ) type ctxKey int const ( ctxKeyUser ctxKey = iota ctxKeyRequestID ctxKeyAudit ) // User 返回当前请求的身份;未经 Authenticate 中间件时是 guest。 func User(ctx context.Context) *auth.User { if u, ok := ctx.Value(ctxKeyUser).(*auth.User); ok { return u } return &auth.User{Role: auth.RoleGuest} } // WithUser 把身份写入 context,供测试或非标准链路使用。 func WithUser(ctx context.Context, u *auth.User) context.Context { return context.WithValue(ctx, ctxKeyUser, u) } // GetRequestID 返回本次请求的 request_id。 func GetRequestID(ctx context.Context) string { if v, ok := ctx.Value(ctxKeyRequestID).(string); ok { return v } return "" } // trustedProxies 是可信代理网段白名单;默认空,表示任何代理头都不可信。 var trustedProxies atomic.Pointer[[]*net.IPNet] // SetTrustedProxies 配置可信代理(IP 或 CIDR 列表);传空表示不信任任何代理头。 func SetTrustedProxies(entries []string) error { nets := make([]*net.IPNet, 0, len(entries)) for _, raw := range entries { entry := strings.TrimSpace(raw) if entry == "" { continue } if strings.Contains(entry, "/") { _, n, err := net.ParseCIDR(entry) if err != nil { return fmt.Errorf("middleware: 非法可信代理 %q: %w", entry, err) } nets = append(nets, n) continue } ip := net.ParseIP(entry) if ip == nil { return fmt.Errorf("middleware: 非法可信代理 %q", entry) } bits := 128 if ip.To4() != nil { bits = 32 } nets = append(nets, &net.IPNet{IP: ip, Mask: net.CIDRMask(bits, bits)}) } trustedProxies.Store(&nets) return nil } func isTrustedProxy(ip net.IP) bool { nets := trustedProxies.Load() if nets == nil || ip == nil { return false } for _, n := range *nets { if n.Contains(ip) { return true } } return false } func remoteHost(addr string) string { host, _, err := net.SplitHostPort(addr) if err != nil { return addr } return host } // ClientIP 解析客户端 IP。 // 只有当直连对端(RemoteAddr)属于可信代理白名单时才采信 X-Forwarded-For / X-Real-IP, // 否则一律用 RemoteAddr——代理头是客户端可伪造的,据此限流等于没有限流。 func ClientIP(r *http.Request) string { remote := net.ParseIP(remoteHost(r.RemoteAddr)) if remote == nil || !isTrustedProxy(remote) { return remoteHost(r.RemoteAddr) } if xff := r.Header.Get("X-Forwarded-For"); xff != "" { parts := strings.Split(xff, ",") // 从右往左取第一个非代理地址:这是可信代理链上离服务端最近的真实客户端。 for i := len(parts) - 1; i >= 0; i-- { ip := net.ParseIP(strings.TrimSpace(parts[i])) if ip == nil || isTrustedProxy(ip) { continue } return ip.String() } // 整条链路都是可信代理时,最左侧即原始客户端。 if ip := net.ParseIP(strings.TrimSpace(parts[0])); ip != nil { return ip.String() } } if ip := net.ParseIP(strings.TrimSpace(r.Header.Get("X-Real-IP"))); ip != nil { return ip.String() } return remoteHost(r.RemoteAddr) }