Просмотр исходного кода

fix(user): invalidate user and token caches when disabling user

When an admin disables/deletes/promotes/demotes a user via ManageUser,
explicitly evict the user cache and all of the user's token caches from
Redis. This prevents a disabled user from continuing to make successful
API requests until the user cache TTL expires, and ensures subsequent
requests reload fresh status from the database.
CaIon 3 недель назад
Родитель
Сommit
9253426223
3 измененных файлов с 52 добавлено и 0 удалено
  1. 17 0
      controller/user.go
  2. 29 0
      model/token.go
  3. 6 0
      model/user_cache.go

+ 17 - 0
controller/user.go

@@ -891,6 +891,11 @@ func ManageUser(c *gin.Context) {
 			})
 			return
 		}
+		// 删除用户后,强制清理 Redis 中所有该用户令牌的缓存,
+		// 避免已缓存的令牌在 TTL 过期前仍能通过 TokenAuth 校验。
+		if err := model.InvalidateUserTokensCache(user.Id); err != nil {
+			common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
+		}
 	case "promote":
 		if myRole != common.RoleRootUser {
 			common.ApiErrorI18n(c, i18n.MsgUserAdminCannotPromote)
@@ -959,6 +964,18 @@ func ManageUser(c *gin.Context) {
 		common.ApiError(c, err)
 		return
 	}
+	// 禁用 / 角色调整后,强制失效用户缓存与其全部令牌缓存,
+	// 避免在 Redis TTL 过期前仍使用旧状态(尤其是禁用后仍可发起请求的问题)。
+	// InvalidateUserCache 会让下一次 GetUserCache 从数据库重新加载,
+	// InvalidateUserTokensCache 则确保令牌侧的缓存也同步刷新。
+	if req.Action == "disable" || req.Action == "promote" || req.Action == "demote" {
+		if err := model.InvalidateUserCache(user.Id); err != nil {
+			common.SysLog(fmt.Sprintf("failed to invalidate user cache for user %d: %s", user.Id, err.Error()))
+		}
+		if err := model.InvalidateUserTokensCache(user.Id); err != nil {
+			common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
+		}
+	}
 	clearUser := model.User{
 		Role:   user.Role,
 		Status: user.Status,

+ 29 - 0
model/token.go

@@ -480,3 +480,32 @@ func GetTokenKeysByIds(ids []int, userId int) ([]Token, error) {
 		Find(&tokens).Error
 	return tokens, err
 }
+
+// InvalidateUserTokensCache 清理指定用户所有令牌在 Redis 中的缓存,
+// 配合 InvalidateUserCache 使用,可在用户被禁用/删除时立即阻断其令牌的请求。
+// 下一次请求将从数据库重新加载令牌及用户状态,从而立即识别出被禁用的用户。
+func InvalidateUserTokensCache(userId int) error {
+	if !common.RedisEnabled {
+		return nil
+	}
+	if userId <= 0 {
+		return errors.New("userId 无效")
+	}
+	var tokens []Token
+	if err := DB.Unscoped().
+		Select("id", commonKeyCol).
+		Where("user_id = ?", userId).
+		Find(&tokens).Error; err != nil {
+		return err
+	}
+	var firstErr error
+	for _, t := range tokens {
+		if t.Key == "" {
+			continue
+		}
+		if err := cacheDeleteToken(t.Key); err != nil && firstErr == nil {
+			firstErr = err
+		}
+	}
+	return firstErr
+}

+ 6 - 0
model/user_cache.go

@@ -57,6 +57,12 @@ func invalidateUserCache(userId int) error {
 	return common.RedisDelKey(getUserCacheKey(userId))
 }
 
+// InvalidateUserCache is the exported version of invalidateUserCache.
+// 供 controller 等上层包在用户状态变更(如禁用、删除、角色变更)后主动清理缓存。
+func InvalidateUserCache(userId int) error {
+	return invalidateUserCache(userId)
+}
+
 // updateUserCache updates all user cache fields using hash
 func updateUserCache(user User) error {
 	if !common.RedisEnabled {