Files
backend/internal/service/yggdrasil_service.go

202 lines
5.3 KiB
Go
Raw Normal View History

package service
import (
"carrotskin/internal/model"
"carrotskin/internal/repository"
"carrotskin/pkg/redis"
"carrotskin/pkg/utils"
"context"
"errors"
"fmt"
"go.uber.org/zap"
"net"
"strings"
"time"
"gorm.io/gorm"
)
// SessionKeyPrefix Redis会话键前缀
const SessionKeyPrefix = "Join_"
// SessionTTL 会话超时时间 - 增加到15分钟
const SessionTTL = 15 * time.Minute
type SessionData struct {
AccessToken string `json:"accessToken"`
UserName string `json:"userName"`
SelectedProfile string `json:"selectedProfile"`
IP string `json:"ip"`
}
// GetUserIDByEmail 根据邮箱返回用户id
func GetUserIDByEmail(db *gorm.DB, Identifier string) (int64, error) {
user, err := repository.FindUserByEmail(Identifier)
if err != nil {
return 0, errors.New("用户不存在")
}
return user.ID, nil
}
// GetProfileByProfileName 根据用户名返回用户id
func GetProfileByProfileName(db *gorm.DB, Identifier string) (*model.Profile, error) {
profile, err := repository.FindProfileByName(Identifier)
if err != nil {
return nil, errors.New("用户角色未创建")
}
return profile, nil
}
// VerifyPassword 验证密码是否一致
func VerifyPassword(db *gorm.DB, password string, Id int64) error {
passwordStore, err := repository.GetYggdrasilPasswordById(Id)
if err != nil {
return errors.New("未生成密码")
}
if passwordStore != password {
return errors.New("密码错误")
}
return nil
}
func GetProfileByUserId(db *gorm.DB, userId int64) (*model.Profile, error) {
profiles, err := repository.FindProfilesByUserID(userId)
if err != nil {
return nil, errors.New("角色查找失败")
}
if len(profiles) == 0 {
return nil, errors.New("角色查找失败")
}
return profiles[0], nil
}
func GetPasswordByUserId(db *gorm.DB, userId int64) (string, error) {
passwordStore, err := repository.GetYggdrasilPasswordById(userId)
if err != nil {
return "", errors.New("yggdrasil密码查找失败")
}
return passwordStore, nil
}
// JoinServer 记录玩家加入服务器的会话信息
func JoinServer(db *gorm.DB, logger *zap.Logger, redisClient *redis.Client, serverId, accessToken, selectedProfile, ip string) error {
// 输入验证
if serverId == "" || accessToken == "" || selectedProfile == "" {
return errors.New("参数不能为空")
}
// 验证serverId格式防止注入攻击
if len(serverId) > 100 || strings.ContainsAny(serverId, "<>\"'&") {
return errors.New("服务器ID格式无效")
}
// 验证IP格式
if ip != "" {
if net.ParseIP(ip) == nil {
return errors.New("IP地址格式无效")
}
}
// 获取和验证Token
token, err := repository.GetTokenByAccessToken(accessToken)
if err != nil {
logger.Error(
"验证Token失败",
zap.Error(err),
zap.String("accessToken", accessToken),
)
return fmt.Errorf("验证Token失败: %w", err)
}
// 格式化UUID并验证与Token关联的配置文件
formattedProfile := utils.FormatUUID(selectedProfile)
if token.ProfileId != formattedProfile {
return errors.New("selectedProfile与Token不匹配")
}
profile, err := repository.FindProfileByUUID(formattedProfile)
if err != nil {
logger.Error(
"获取Profile失败",
zap.Error(err),
zap.String("uuid", formattedProfile),
)
return fmt.Errorf("获取Profile失败: %w", err)
}
// 创建会话数据
data := SessionData{
AccessToken: accessToken,
UserName: profile.Name,
SelectedProfile: formattedProfile,
IP: ip,
}
// 序列化会话数据
marshaledData, err := json.Marshal(data)
if err != nil {
logger.Error(
"[ERROR]序列化会话数据失败",
zap.Error(err),
)
return fmt.Errorf("序列化会话数据失败: %w", err)
}
// 存储会话数据到Redis
sessionKey := SessionKeyPrefix + serverId
ctx := context.Background()
if err = redisClient.Set(ctx, sessionKey, marshaledData, SessionTTL); err != nil {
logger.Error(
"保存会话数据失败",
zap.Error(err),
zap.String("serverId", serverId),
)
return fmt.Errorf("保存会话数据失败: %w", err)
}
logger.Info(
"玩家成功加入服务器",
zap.String("username", profile.Name),
zap.String("serverId", serverId),
)
return nil
}
// HasJoinedServer 验证玩家是否已经加入了服务器
func HasJoinedServer(logger *zap.Logger, redisClient *redis.Client, serverId, username, ip string) error {
if serverId == "" || username == "" {
return errors.New("服务器ID和用户名不能为空")
}
// 设置超时上下文
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// 从Redis获取会话数据
sessionKey := SessionKeyPrefix + serverId
data, err := redisClient.GetBytes(ctx, sessionKey)
if err != nil {
logger.Error("[ERROR] 获取会话数据失败:", zap.Error(err), zap.Any("serverId:", serverId))
return fmt.Errorf("获取会话数据失败: %w", err)
}
// 反序列化会话数据
var sessionData SessionData
if err = json.Unmarshal(data, &sessionData); err != nil {
logger.Error("[ERROR] 解析会话数据失败: ", zap.Error(err))
return fmt.Errorf("解析会话数据失败: %w", err)
}
// 验证用户名
if sessionData.UserName != username {
return errors.New("用户名不匹配")
}
// 验证IP(如果提供)
if ip != "" && sessionData.IP != ip {
return errors.New("IP地址不匹配")
}
return nil
}