Skip to content

feat(apikey): 支持原地轮换 API Key,保留 Key ID 和计费历史 - #6439

Open
xpflying wants to merge 1 commit into
Wei-Shaw:mainfrom
xpflying:feat/api-key-rotate-in-place
Open

feat(apikey): 支持原地轮换 API Key,保留 Key ID 和计费历史#6439
xpflying wants to merge 1 commit into
Wei-Shaw:mainfrom
xpflying:feat/api-key-rotate-in-place

Conversation

@xpflying

@xpflying xpflying commented Aug 31, 2026

Copy link
Copy Markdown

关联 Issue

Closes #6409

背景

API Key 泄露后目前只能禁用旧 Key 再新建,新 Key 会拿到新的 api_key_id,历史用量和计费记录被拆分。本 PR 在不创建新记录的前提下原地替换鉴权凭据,保留原 Key ID、用量、计费历史和全部配置。

实现内容

后端

  • 用户端新增 POST /api/v1/keys/:id/rotate,只允许轮换自己的 Key;管理员端新增 POST /api/v1/admin/api-keys/:id/rotate,沿用现有管理员权限。
  • 轮换使用数据库 compare-and-swap:只有 Key ID、未删除状态和旧凭据同时匹配时才更新,并发轮换只有一个能成功,冲突方返回 409。
  • 新凭据复用自动创建 Key 的 GenerateKey()crypto/rand 32 字节随机数 + 配置前缀),数据库只更新 keylast_rotated_atupdated_at,其余字段全部保留。
  • 更新成功后清理新旧凭据的进程内缓存和 Redis 缓存,并通过现有 Pub/Sub 通知其他实例;现有数据库触发器会在同一事务把新旧凭据的 SHA-256 缓存键写入持久失效 outbox 兜底,缓存与消息中不出现明文。
  • 轮换后的 Key 沿用平台现有展示行为:列表/详情接口照常返回完整凭据,与新建 Key 一致;响应新增 last_rotated_at 字段记录轮换时间。
  • 审计日志记录操作者与目标 Key ID(管理员操作额外记录目标用户 ID),不落任何明文凭据。

前端

  • 用户端 Keys 页面与管理员端用户 Key 弹窗均新增“轮换”操作:二次确认 + 风险提示 + 勾选确认,成功后弹窗展示新凭据并提供复制。
  • 原有的复制、使用、导入等操作布局和行为保持不变。
  • 中英文文案齐备。

数据

  • 新增迁移 232_api_keys_last_rotated_at.sql(幂等,ADD COLUMN IF NOT EXISTS)。

测试

  • 服务层单测:记录保留、凭据格式、所有权校验、并发冲突、仓储能力、新旧缓存失效。
  • 仓储集成测试:数据库 CAS、字段保留、旧 Key 立即失效、新 Key 生效、outbox 哈希入队。
  • 处理器、DTO、审计(含明文不落审计库断言)、API contract、迁移结构测试随接口更新。
  • 前端组件测试覆盖用户/管理员确认流程、新凭据展示和列表刷新。
  • go test ./...go vetvue-tsc、ESLint、Vitest、Vite 生产构建均通过。

部署说明

  1. 启用新版本前执行迁移 232_api_keys_last_rotated_at.sql(随现有迁移流程自动执行)。
  2. 多实例环境建议统一升级版本,避免旧实例不认识 last_rotated_at 字段。
  3. 发布后可分别用旧/新 Key 发起鉴权请求验证:旧 Key 返回未授权,新 Key 正常。

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA. ✅
Posted by the CLA Assistant Lite bot.

@xpflying

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Aug 31, 2026
Add POST /api/v1/keys/:id/rotate and POST /api/v1/admin/api-keys/:id/rotate
to replace an API key credential without creating a new record. The key ID,
owner, name, group, quota, rate limits, IP rules, expiry, usage and billing
history stay unchanged; only the secret is regenerated.

- Atomic compare-and-swap update so concurrent rotations cannot both succeed
- Invalidate old and new credentials in process/Redis auth caches and notify
  other instances via existing pub/sub; the DB trigger enqueues both hashes
  into the durable invalidation outbox
- Rotated keys keep the platform's existing display behavior: list/detail
  responses return the full credential and copy/use/import stay available;
  API responses gain last_rotated_at recording the rotation time
- Frontend adds confirm dialog and new-credential display with copy for
  both user and admin views
- Audit logs record operator and key ID (admin also records target user),
  never plaintext secrets

Closes Wei-Shaw#6409
@xpflying
xpflying force-pushed the feat/api-key-rotate-in-place branch from bf19c73 to a95f068 Compare August 31, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

功能建议:支持原地轮换用户 API 密钥,保留 Key ID 和计费历史

1 participant