fix(token-cache): 版本过时时使用最新token而非旧token

上次修复(2665230)只阻止了写入缓存,但仍返回旧token导致403。
现在版本过时时直接使用DB中的最新token返回。

- 重构 IsTokenVersionStale 为 CheckTokenVersion,返回最新account
- 消除重复DB查询,复用版本检查时已获取的account
This commit is contained in:
shaw
2026-01-23 10:29:52 +08:00
parent 4bd3dbf2ce
commit dac6bc2228
5 changed files with 118 additions and 70 deletions

View File

@@ -65,22 +65,24 @@ func (c *CompositeTokenCacheInvalidator) InvalidateToken(ctx context.Context, ac
return nil
}
// IsTokenVersionStale 检查 account 的 token 版本是否已过时
// CheckTokenVersion 检查 account 的 token 版本是否已过时,并返回最新的 account
// 用于解决异步刷新任务与请求线程的竞态条件:
// 如果刷新任务已更新 token 并删除缓存,此时请求线程的旧 account 对象不应写入缓存
//
// 返回 true 表示 token 已过时不应缓存false 表示可以缓存
func IsTokenVersionStale(ctx context.Context, account *Account, repo AccountRepository) bool {
// 返回值:
// - latestAccount: 从 DB 获取的最新 account如果查询失败则返回 nil
// - isStale: true 表示 token 已过时(应使用 latestAccountfalse 表示可以使用当前 account
func CheckTokenVersion(ctx context.Context, account *Account, repo AccountRepository) (latestAccount *Account, isStale bool) {
if account == nil || repo == nil {
return false
return nil, false
}
currentVersion := account.GetCredentialAsInt64("_token_version")
latestAccount, err := repo.GetByID(ctx, account.ID)
if err != nil || latestAccount == nil {
// 查询失败,默认允许缓存
return false
// 查询失败,默认允许缓存,不返回 latestAccount
return nil, false
}
latestVersion := latestAccount.GetCredentialAsInt64("_token_version")
@@ -91,12 +93,12 @@ func IsTokenVersionStale(ctx context.Context, account *Account, repo AccountRepo
slog.Debug("token_version_stale_no_current_version",
"account_id", account.ID,
"latest_version", latestVersion)
return true
return latestAccount, true
}
// 情况2: 两边都没有版本号,说明从未被异步刷新过,允许缓存
if currentVersion == 0 && latestVersion == 0 {
return false
return latestAccount, false
}
// 情况3: 比较版本号,如果 DB 中的版本更新,当前 account 已过时
@@ -105,8 +107,8 @@ func IsTokenVersionStale(ctx context.Context, account *Account, repo AccountRepo
"account_id", account.ID,
"current_version", currentVersion,
"latest_version", latestVersion)
return true
return latestAccount, true
}
return false
return latestAccount, false
}