package service import ( "context" "github.com/google/uuid" "github.com/ocdp/cluster-service/internal/domain/entity" "github.com/ocdp/cluster-service/internal/domain/repository" ) // AuthService 认证领域服务 type AuthService struct { userRepo repository.UserRepository passwordHasher PasswordHasher tokenGenerator TokenGenerator } // PasswordHasher 密码哈希接口 type PasswordHasher interface { Hash(password string) (string, error) Verify(password, hash string) error } // TokenGenerator Token 生成器接口 type TokenGenerator interface { Generate(userID, username string) (accessToken, refreshToken string, err error) Verify(token string) (userID, username string, err error) VerifyWithIssuedAt(token string) (userID, username string, issuedAt int64, err error) Refresh(refreshToken string) (newAccessToken string, err error) } // NewAuthService 创建认证服务 func NewAuthService( userRepo repository.UserRepository, passwordHasher PasswordHasher, tokenGenerator TokenGenerator, ) *AuthService { return &AuthService{ userRepo: userRepo, passwordHasher: passwordHasher, tokenGenerator: tokenGenerator, } } // Register 注册新用户(仅需用户名和密码,邮箱将自动补全) func (s *AuthService) Register(ctx context.Context, username, password string) (*entity.User, error) { // 检查用户是否已存在 existingUser, _ := s.userRepo.GetByUsername(ctx, username) if existingUser != nil { return nil, entity.ErrUserExists } // 哈希密码 passwordHash, err := s.passwordHasher.Hash(password) if err != nil { return nil, err } // 默认生成占位邮箱,避免数据库约束失败 email := username + "@local.ocdp" // 创建用户 user := entity.NewUser(username, passwordHash, email) user.ID = uuid.New().String() if err := user.Validate(); err != nil { return nil, err } if err := s.userRepo.Create(ctx, user); err != nil { return nil, err } return user, nil } // Login 用户登录 func (s *AuthService) Login(ctx context.Context, username, password string) (accessToken, refreshToken string, err error) { // 查找用户 user, err := s.userRepo.GetByUsername(ctx, username) if err != nil { return "", "", entity.ErrUserNotFound } // 验证密码 if err := s.passwordHasher.Verify(password, user.PasswordHash); err != nil { return "", "", entity.ErrInvalidPassword } // 生成 Token accessToken, refreshToken, err = s.tokenGenerator.Generate(user.ID, user.Username) if err != nil { return "", "", err } return accessToken, refreshToken, nil } // RefreshToken 刷新 Token func (s *AuthService) RefreshToken(ctx context.Context, refreshToken string) (string, error) { return s.tokenGenerator.Refresh(refreshToken) } // GetUserByID 根据 ID 获取用户 func (s *AuthService) GetUserByID(ctx context.Context, id string) (*entity.User, error) { return s.userRepo.GetByID(ctx, id) } // VerifyAccessToken 验证 Access Token(包括 revoked_after 检查) func (s *AuthService) VerifyAccessToken(ctx context.Context, token string) (userID, username string, err error) { // 1. JWT 自验证 userID, username, issuedAt, err := s.tokenGenerator.VerifyWithIssuedAt(token) if err != nil { return "", "", err } // 2. 检查用户级别的撤销时间 user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return "", "", entity.ErrUserNotFound } // 3. 如果 Token 签发时间早于 revoked_after,则失效 if issuedAt < user.RevokedAfter.Unix() { return "", "", entity.ErrTokenRevoked } return userID, username, nil } // ChangePassword 修改密码(会触发全局登出) func (s *AuthService) ChangePassword(ctx context.Context, userID, oldPassword, newPassword string) error { // 1. 获取用户 user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return entity.ErrUserNotFound } // 2. 验证旧密码 if err := s.passwordHasher.Verify(oldPassword, user.PasswordHash); err != nil { return entity.ErrInvalidPassword } // 3. 哈希新密码 newPasswordHash, err := s.passwordHasher.Hash(newPassword) if err != nil { return err } // 4. 更新密码(会自动触发 revoked_after 更新) user.UpdatePassword(newPasswordHash) // 5. 保存到数据库 return s.userRepo.Update(ctx, user) } // ForceLogoutAll 强制全局登出(管理员操作) func (s *AuthService) ForceLogoutAll(ctx context.Context, userID string) error { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return entity.ErrUserNotFound } user.RevokeAllTokens() return s.userRepo.Update(ctx, user) }