2026-05-02 13:02:38 +08:00

134 lines
4.0 KiB
Go

package auth
import (
"errors"
"time"
"hyapp-admin-server/internal/config"
"hyapp-admin-server/internal/model"
"hyapp-admin-server/internal/modules/shared"
"hyapp-admin-server/internal/repository"
authcore "hyapp-admin-server/internal/service"
"gorm.io/gorm"
)
type AuthFlowService struct {
store *repository.Store
auth *authcore.AuthService
cfg config.Config
}
type LoginInput struct {
Username string
Password string
IP string
UserAgent string
}
type LoginResult struct {
AccessToken string
RefreshToken string
ExpiresAt time.Time
User model.User
Permissions []string
}
func NewService(store *repository.Store, auth *authcore.AuthService, cfg config.Config) *AuthFlowService {
return &AuthFlowService{store: store, auth: auth, cfg: cfg}
}
func (s *AuthFlowService) Login(input LoginInput) (LoginResult, error) {
user, err := s.store.FindUserByUsername(input.Username)
if err != nil || !authcore.CheckPassword(user.PasswordHash, input.Password) {
s.loginLog(input, nil, "failed", "用户名或密码错误")
return LoginResult{}, errors.New("用户名或密码错误")
}
if user.Status != model.UserStatusActive {
s.loginLog(input, &user.ID, "failed", "账号不可登录")
return LoginResult{}, errors.New("账号不可登录")
}
permissions := repository.PermissionsForUser(*user)
accessToken, expiresAt, err := s.auth.GenerateAccessToken(user.ID, user.Username, permissions)
if err != nil {
return LoginResult{}, err
}
refreshToken, refreshHash, err := authcore.NewRefreshToken()
if err != nil {
return LoginResult{}, err
}
if err := s.store.CreateRefreshToken(model.RefreshToken{
UserID: user.ID,
TokenHash: refreshHash,
UserAgent: input.UserAgent,
IP: input.IP,
ExpiresAt: time.Now().Add(s.cfg.RefreshTokenTTL),
}); err != nil {
return LoginResult{}, err
}
_ = s.store.TouchUserLogin(user.ID)
s.loginLog(input, &user.ID, "success", "登录成功")
return LoginResult{AccessToken: accessToken, RefreshToken: refreshToken, ExpiresAt: expiresAt, User: *user, Permissions: permissions}, nil
}
func (s *AuthFlowService) Logout(refreshToken string) error {
if refreshToken == "" {
return nil
}
return s.store.RevokeRefreshToken(authcore.HashToken(refreshToken))
}
func (s *AuthFlowService) Refresh(refreshToken string) (LoginResult, error) {
if refreshToken == "" {
return LoginResult{}, errors.New("刷新会话不存在")
}
stored, err := s.store.FindRefreshToken(authcore.HashToken(refreshToken))
if err != nil {
return LoginResult{}, errors.New("刷新会话已失效")
}
user, err := s.store.FindUserByID(stored.UserID)
if err != nil || user.Status != model.UserStatusActive {
return LoginResult{}, errors.New("账号不可登录")
}
permissions := repository.PermissionsForUser(*user)
accessToken, expiresAt, err := s.auth.GenerateAccessToken(user.ID, user.Username, permissions)
if err != nil {
return LoginResult{}, err
}
return LoginResult{AccessToken: accessToken, ExpiresAt: expiresAt, User: *user, Permissions: permissions}, nil
}
func (s *AuthFlowService) Me(actor shared.Actor) (*model.User, []string, error) {
user, err := s.store.FindUserByID(actor.UserID)
if err != nil {
return nil, nil, err
}
return user, repository.PermissionsForUser(*user), nil
}
func (s *AuthFlowService) ChangePassword(actor shared.Actor, oldPassword string, newPassword string) error {
user, err := s.store.FindUserByID(actor.UserID)
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("用户不存在")
}
if err != nil {
return err
}
if !authcore.CheckPassword(user.PasswordHash, oldPassword) {
return errors.New("旧密码不正确")
}
return s.store.ResetUserPassword(user.ID, newPassword)
}
func (s *AuthFlowService) loginLog(input LoginInput, userID *uint, status string, message string) {
_ = s.store.CreateLoginLog(model.LoginLog{
Username: input.Username,
UserID: userID,
IP: input.IP,
UserAgent: input.UserAgent,
Status: status,
Message: message,
})
}