API密钥轮换需支持新旧并存与平滑过渡,核心是允许多token共存、加revoked_at字段、重写认证逻辑,并通过灰度下线、响应头提示、定时清理及监控告警保障安全更新。

API密钥轮换不是改个字段就行
直接在数据库里 UPDATE api_tokens 表或重写 token 字段,会导致正在使用的请求瞬间 401。Laravel 的 HasApiTokens trait 和 Passport 的 token 验证是强依赖服务端存储的,轮换必须支持「新旧并存」和「平滑过渡」。
核心思路是:让一个用户能同时拥有多个有效 token,每个带独立过期时间、用途标记和状态字段,验证逻辑要主动过滤掉已废弃的 token。
- 必须加
revoked_at时间戳字段(非布尔值),方便审计和精确作废 - 不要用
expires_at做唯一判断——有些 token 设计为永不过期(如后台管理密钥),得靠状态字段兜底 - Passport 默认不支持多 token 并存校验,得重写
TokenGuard::authenticate()或改用自定义中间件
Laravel Passport 怎么存多个 API 密钥
Passport 默认只允许一个 Personal Access Token(PersonalAccessClient 类型),但你可以绕过限制,手动插入多条 OauthAccessToken 记录,前提是自己管好 scope、client_id、user_id 和状态字段。
推荐做法:不用 Passport 的 createToken(),而是调用底层 PersonalAccessToken::create(),并显式传入额外字段:
$token = $user->tokens()->create([
'name' => 'mobile-app-v2',
'scopes' => ['read-api', 'submit-data'],
'expires_at' => now()->addDays(90),
'revoked_at' => null,
'is_primary' => false
]);
-
is_primary是自建字段,用于标识当前默认密钥,避免客户端每次都要指定 token 名 - 所有 token 共享同一个
client_id(即同一个应用),但id和token值必须唯一 - 注意迁移时加索引:
user_id+revoked_at组合查询高频,否则列表页会慢
怎么让旧密钥自动停用又不中断请求
关键不在生成新密钥,而在「灰度下线」——旧密钥不是立刻删,而是进入 grace period,在此期间仍可验证,但返回响应头提示客户端尽快刷新:
- 在认证中间件里检查
revoked_at是否为空,且expires_at是否未过期 - 若
revoked_at在过去 7 天内(比如设为now()->subDays(7)),则返回 HTTP 200 + 自定义头X-Token-Deprecated: true - 绝不返回 401,除非
revoked_at早于 7 天前 或expires_at已过期 - 前端收到
X-Token-Deprecated后,下次请求附带refresh_token换新,后端此时才把旧 token 的revoked_at设为真实时间
定时任务执行轮换时容易漏掉什么
php artisan schedule:run 跑的命令如果只查 expires_at 就删 token,等于没轮换——它跳过了「已通知但尚未切换」的灰度期。
真正该清理的是那些满足以下全部条件的记录:
-
revoked_at不为空 -
revoked_at subDays(7)(留足客户端缓冲) - 没有关联未完成的异步任务(比如该 token 发起的 webhook 还在重试队列里)
建议加一层保护:清理前先查 DB::table('jobs')->where('payload', 'like', "%{$token->id}%")->count(),有残留 job 就跳过本次清理。
轮换机制最麻烦的从来不是生成新 token,而是确认所有下游系统(尤其是第三方回调、IoT 设备固件、离线客户端)都完成了密钥更新。这个没法全靠代码兜住,得靠 revoked_at + 日志埋点 + 监控告警三者联动。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











