package service import ( apperrors "carrotskin/internal/errors" "carrotskin/pkg/redis" "context" "fmt" "net" "strings" "time" "go.uber.org/zap" ) // SessionKeyPrefix Redis会话键前缀 const SessionKeyPrefix = "Join_" // SessionTTL 会话超时时间 - 增加到15分钟 const SessionTTL = 15 * time.Minute // SessionData 会话数据 type SessionData struct { AccessToken string `json:"accessToken"` UserName string `json:"userName"` SelectedProfile string `json:"selectedProfile"` IP string `json:"ip"` } // SessionService 会话管理服务接口 type SessionService interface { // CreateSession 创建服务器会话 CreateSession(ctx context.Context, serverID, accessToken, username, profileUUID, ip string) error // GetSession 获取会话数据 GetSession(ctx context.Context, serverID string) (*SessionData, error) // ValidateSession 验证会话(用户名和IP) ValidateSession(ctx context.Context, serverID, username, ip string) error } // yggdrasilSessionService 会话服务实现 type yggdrasilSessionService struct { redis *redis.Client logger *zap.Logger } // NewSessionService 创建会话服务实例 func NewSessionService(redisClient *redis.Client, logger *zap.Logger) SessionService { return &yggdrasilSessionService{ redis: redisClient, logger: logger, } } // ValidateServerID 验证服务器ID格式 func ValidateServerID(serverID string) error { if serverID == "" { return apperrors.ErrInvalidServerID } if len(serverID) > 100 || strings.ContainsAny(serverID, "<>\"'&") { return apperrors.ErrInvalidServerID } return nil } // ValidateIP 验证IP地址格式 func ValidateIP(ip string) error { if ip == "" { return nil // IP是可选的 } if net.ParseIP(ip) == nil { return apperrors.ErrIPMismatch } return nil } // CreateSession 创建服务器会话 func (s *yggdrasilSessionService) CreateSession(ctx context.Context, serverID, accessToken, username, profileUUID, ip string) error { // 输入验证 if err := ValidateServerID(serverID); err != nil { return err } if accessToken == "" { return apperrors.ErrInvalidAccessToken } if username == "" { return apperrors.ErrUsernameMismatch } if profileUUID == "" { return apperrors.ErrProfileMismatch } if err := ValidateIP(ip); err != nil { return err } // 创建会话数据 data := SessionData{ AccessToken: accessToken, UserName: username, SelectedProfile: profileUUID, IP: ip, } // 序列化会话数据 marshaledData, err := json.Marshal(data) if err != nil { s.logger.Error("序列化会话数据失败", zap.Error(err), zap.String("serverID", serverID), ) return fmt.Errorf("序列化会话数据失败: %w", err) } // 存储会话数据到Redis sessionKey := SessionKeyPrefix + serverID if err = s.redis.Set(ctx, sessionKey, marshaledData, SessionTTL); err != nil { s.logger.Error("保存会话数据失败", zap.Error(err), zap.String("serverID", serverID), ) return fmt.Errorf("保存会话数据失败: %w", err) } s.logger.Info("会话创建成功", zap.String("username", username), zap.String("serverID", serverID), ) return nil } // GetSession 获取会话数据 func (s *yggdrasilSessionService) GetSession(ctx context.Context, serverID string) (*SessionData, error) { if err := ValidateServerID(serverID); err != nil { return nil, err } // 从Redis获取会话数据 sessionKey := SessionKeyPrefix + serverID data, err := s.redis.GetBytes(ctx, sessionKey) if err != nil { s.logger.Error("获取会话数据失败", zap.Error(err), zap.String("serverID", serverID), ) return nil, fmt.Errorf("获取会话数据失败: %w", err) } // 反序列化会话数据 var sessionData SessionData if err = json.Unmarshal(data, &sessionData); err != nil { s.logger.Error("解析会话数据失败", zap.Error(err), zap.String("serverID", serverID), ) return nil, fmt.Errorf("解析会话数据失败: %w", err) } return &sessionData, nil } // ValidateSession 验证会话(用户名和IP) func (s *yggdrasilSessionService) ValidateSession(ctx context.Context, serverID, username, ip string) error { if serverID == "" || username == "" { return apperrors.ErrSessionMismatch } sessionData, err := s.GetSession(ctx, serverID) if err != nil { return apperrors.ErrSessionNotFound } // 验证用户名 if sessionData.UserName != username { return apperrors.ErrUsernameMismatch } // 验证IP(如果提供) if ip != "" && sessionData.IP != ip { return apperrors.ErrIPMismatch } return nil }