This commit is contained in:
2026-08-16 09:02:39 +08:00
parent c554152f4f
commit f7f6db8695
105 changed files with 2535 additions and 948 deletions
+25 -27
View File
@@ -42,10 +42,16 @@ func (jwtBlacklistPO) TableName() string { return "jwt_blacklists" }
func (r *apiTokenRepo) UserHasAuthority(ctx context.Context, userID, authorityID uint) (*biz.User, bool, error) {
var po userPO
if err := r.data.gormDB.WithContext(ctx).First(&po, userID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, false, errors.New("用户不存在")
}
return nil, false, err
}
var count int64
err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).Where("sys_user_id = ? AND sys_authority_authority_id = ?", userID, authorityID).Count(&count).Error
err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).
Joins("JOIN sys_authorities ON sys_authorities.authority_id = sys_user_authority.sys_authority_authority_id").
Where("sys_user_authority.sys_user_id = ? AND sys_user_authority.sys_authority_authority_id = ? AND sys_authorities.deleted_at IS NULL", userID, authorityID).
Count(&count).Error
if err != nil {
return nil, false, err
}
@@ -106,22 +112,20 @@ func (r *apiTokenRepo) DisableAPIToken(ctx context.Context, id uint) (string, er
return po.Token, r.data.gormDB.WithContext(ctx).Model(&po).Update("status", false).Error
}
func (r *apiTokenRepo) DisableAndBlacklistAPIToken(ctx context.Context, id uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var po apiTokenPO
if err := tx.First(&po, id).Error; err != nil {
return err
}
// Blacklist the token before disabling it while making both writes atomic.
if err := tx.Create(&jwtBlacklistPO{JWT: po.Token}).Error; err != nil {
return err
}
return tx.Model(&po).Update("status", false).Error
})
// Persist the blacklist row first, then mark the
// API-token record disabled. The two writes are intentionally separate in
// the reference implementation, so a storage error has the same observable
// result here.
var po apiTokenPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return err
}
if err := r.BlacklistToken(ctx, po.Token); err != nil {
return err
}
return r.data.gormDB.WithContext(ctx).Model(&po).Update("status", false).Error
}
func (r *apiTokenRepo) BlacklistToken(ctx context.Context, token string) error {
if token == "" {
return errors.New("token is empty")
}
return r.data.gormDB.WithContext(ctx).Create(&jwtBlacklistPO{JWT: token}).Error
}
func (r *apiTokenRepo) IsTokenDisabled(ctx context.Context, token string) (bool, error) {
@@ -129,16 +133,10 @@ func (r *apiTokenRepo) IsTokenDisabled(ctx context.Context, token string) (bool,
if err := r.data.gormDB.WithContext(ctx).Model(&jwtBlacklistPO{}).Where("jwt = ?", token).Count(&blacklistCount).Error; err != nil {
return false, err
}
if blacklistCount > 0 {
return true, nil
}
var po apiTokenPO
result := r.data.gormDB.WithContext(ctx).Where("token = ?", token).Limit(1).Find(&po)
if result.Error != nil {
return false, result.Error
}
if result.RowsAffected == 0 {
return false, nil
}
return !po.Status || time.Now().After(po.ExpiresAt), nil
// The JWT middleware consults only jwt_blacklists and lets JWT parsing
// report expiry. API-token deletion writes this row before changing the
// token status, so consulting status/ExpiresAt here would produce a
// different error message and would make manually-disabled tokens behave
// unlike tokens issued through the regular login flow.
return blacklistCount > 0, nil
}