Files
NetworkAuth/services/refresh_token.go

122 lines
3.3 KiB
Go
Raw Normal View History

package services
import (
"NetworkAuth/database"
"NetworkAuth/models"
"errors"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// RefreshTokenService 提供 refreshToken 的持久化、轮换、撤销等业务能力
type RefreshTokenService struct{}
var refreshTokenService = &RefreshTokenService{}
// GetRefreshTokenService 单例获取
func GetRefreshTokenService() *RefreshTokenService {
return refreshTokenService
}
// NewJTI 生成新的 jti
func (s *RefreshTokenService) NewJTI() string {
return uuid.New().String()
}
// NewFamilyID 生成新的 family_id每次登录都新建
func (s *RefreshTokenService) NewFamilyID() string {
return uuid.New().String()
}
// Create 持久化一条 refreshToken 记录
// - 登录场景:传入新的 familyID + absolutenow + 绝对过期天数)
// - 刷新场景:复用旧 familyID 与旧 absolute保证滑动续期不能突破上限
func (s *RefreshTokenService) Create(jti, familyID, userUUID, userType string,
expiresAt, absoluteExpiresAt time.Time, ua, ip string) error {
db, err := database.GetDB()
if err != nil {
return err
}
rec := models.RefreshToken{
JTI: jti,
FamilyID: familyID,
UserUUID: userUUID,
UserType: userType,
IssuedAt: time.Now(),
ExpiresAt: expiresAt,
AbsoluteExpiresAt: absoluteExpiresAt,
UserAgent: ua,
IP: ip,
}
return db.Create(&rec).Error
}
// FindByJTI 根据 jti 查询
func (s *RefreshTokenService) FindByJTI(jti string) (*models.RefreshToken, error) {
db, err := database.GetDB()
if err != nil {
return nil, err
}
var rec models.RefreshToken
if err := db.Where("jti = ?", jti).First(&rec).Error; err != nil {
return nil, err
}
return &rec, nil
}
// RevokeFamily 撤销整个 family 下所有未撤销的 refreshToken用于重用检测/登出)
func (s *RefreshTokenService) RevokeFamily(familyID string) error {
db, err := database.GetDB()
if err != nil {
return err
}
return db.Model(&models.RefreshToken{}).
Where("family_id = ? AND revoked = ?", familyID, false).
Update("revoked", true).Error
}
// RevokeByJTI 撤销单条 refreshToken一般在轮换时使用 Rotate
func (s *RefreshTokenService) RevokeByJTI(jti string) error {
db, err := database.GetDB()
if err != nil {
return err
}
return db.Model(&models.RefreshToken{}).
Where("jti = ?", jti).
Update("revoked", true).Error
}
// Rotate 标记旧 jti 为已撤销,并记录被替换为新 jti
func (s *RefreshTokenService) Rotate(oldJTI, newJTI string) error {
db, err := database.GetDB()
if err != nil {
return err
}
return db.Model(&models.RefreshToken{}).
Where("jti = ?", oldJTI).
Updates(map[string]interface{}{
"revoked": true,
"replaced_by": newJTI,
}).Error
}
// CleanupExpired 清理过期且过期时间早于 retentionDays 天前的记录
func (s *RefreshTokenService) CleanupExpired(retentionDays int) error {
db, err := database.GetDB()
if err != nil {
return err
}
cutoff := time.Now().AddDate(0, 0, -retentionDays)
return db.Where("expires_at < ?", cutoff).Delete(&models.RefreshToken{}).Error
}
// ErrRefreshNotFound refreshToken 不存在
var ErrRefreshNotFound = errors.New("refresh token not found")
// IsNotFound 判断是否为记录未找到错误
func IsNotFound(err error) bool {
return errors.Is(err, gorm.ErrRecordNotFound)
}