package response import ( "context" "lone-services/pkg/utils" "net/http" "net/url" "strconv" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) type ctxKey string const ( CtxUserId ctxKey = "X-User-Id" CtxUserName ctxKey = "X-User-Name" CtxRefresh ctxKey = "X-Refresh" CtxClientIP ctxKey = "X-Client-Ip" CtxUserAgent ctxKey = "X-User-Agent" ) type UserInfo struct { ID int64 Name string RawUID string Refresh string ClientIP string UserAgent string Valid bool } func GetUserInfo(ctx context.Context) UserInfo { rawUID, _ := ctx.Value(CtxUserId).(string) name, _ := ctx.Value(CtxUserName).(string) refresh, _ := ctx.Value(CtxRefresh).(string) clientIP, _ := ctx.Value(CtxClientIP).(string) userAgent, _ := ctx.Value(CtxUserAgent).(string) info := UserInfo{ RawUID: rawUID, Name: name, Refresh: refresh, ClientIP: clientIP, UserAgent: userAgent, } if rawUID == "" { return info } uid, err := strconv.ParseInt(rawUID, 10, 64) if err != nil { info.Valid = false return info } info.ID = uid info.Valid = true return info } // getRealClientIP 获取真实客户端IP,优先 X‑Forwarded‑For,其次 X‑Real‑IP,最后 RemoteAddr func getRealClientIP(r *http.Request) string { xff := r.Header.Get("X-Forwarded-For") if xff != "" { return xff } xri := r.Header.Get("X-Real-Ip") if xri != "" { return xri } return r.RemoteAddr } func UserReadMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { newCtx := r.Context() newCtx = context.WithValue(newCtx, CtxUserId, r.Header.Get("X-User-Id")) newCtx = context.WithValue(newCtx, CtxUserName, r.Header.Get("X-User-Name")) newCtx = context.WithValue(newCtx, CtxRefresh, r.Header.Get("X-Refresh")) newCtx = context.WithValue(newCtx, CtxClientIP, getRealClientIP(r)) newCtx = context.WithValue(newCtx, CtxUserAgent, r.UserAgent()) next(w, r.WithContext(newCtx)) } } func UserClientInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { user := GetUserInfo(ctx) md := metadata.New(map[string]string{}) if user.Valid { md.Set("x-user-id", user.RawUID) md.Set("x-user-name", url.QueryEscape(user.Name)) } if user.Refresh != utils.StringEmpty { md.Set("x-refresh", user.Refresh) } if user.ClientIP != utils.StringEmpty { md.Set("x-client-ip", user.ClientIP) } if user.UserAgent != utils.StringEmpty { md.Set("x-user-agent", url.QueryEscape(user.UserAgent)) } ctx = metadata.NewOutgoingContext(ctx, md) return invoker(ctx, method, req, reply, cc, opts...) }