Files
ocdp-go/backend/internal/domain/service/auth_service.go
mangomqy c5e51ed069 ocdp v1
2025-11-13 02:54:06 +00:00

166 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}