134 lines
4.0 KiB
Go
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,
|
|
})
|
|
}
|