Claude Code Router 的 API Keys 管理:网关客户端密钥、有效期与本地限额配置指南
【免费下载链接】claude-code-routerOne local control plane for every AI agent: route across models, fuse new capabilities, orchestrate tools, and stay fully in control.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-code-router
导读
CCR(Claude Code Router)作为统一本地控制平面,会代理所有客户端(Claude Code、Codex、各类自动化脚本)的上游请求。本文围绕 CCR 图形界面中的API keys配置模块,系统讲解客户端访问密钥的创建、编辑、删除,以及可选的过期时间与本地限额(Requests / Tokens / Images)设置,并结合仓库源码剖析密钥的存储、鉴权与限额计量的底层实现,帮助你为不同客户端、团队或 CI 自动化分配独立、可回收、带防护的网关访问凭证。
API keys 在 CCR 中的定位
在 CCR 网关架构中,客户端并不直接携带上游提供商(如 Anthropic、OpenAI 等)的凭证访问模型,而是通过网关统一出口完成路由、模型切换与工具编排。因此网关自身需要一套独立的客户端访问凭证体系,也就是本文要讲的 API keys:它标识"谁在使用网关",并可在网关侧施加过期时间与本地限额。
从 入口鉴权实现 可以看到,网关对每个请求都会先执行authorize():
- 当没有任何 API key 被初始化时,直接返回
403,错误信息为 "CCR API key is not initialized. Save a gateway API key or restart CCR to generate one."; - 当请求缺少
Authorization: Bearer <key>头时,返回401,信息为 "API key is missing."; - 当 key无效时返回
401"Invalid API key."; - 当 key已过期时返回
401"API key is expired."。
也就是说,API keys 是网关访问控制的第一道闸门,删除或过期一个 key,客户端将立即失去网关访问能力。
列表页字段速览
在 CCR 配置界面的 API keys 列表中,每个条目展示以下信息:
| 字段 | 能力说明 |
|---|---|
| Search API keys | 按 key 名称或 key 值过滤列表,便于在 key 数量较多时快速定位 |
| Add API key | 打开创建对话框,生成一个新的客户端访问 key |
| Name | key 的显示名称,用于标识客户端、团队、用途或自动化任务 |
| Key | 掩码显示的访问 key,通过Copy API key复制完整值 |
| Expires | 过期时间;过期后客户端无法再使用该 key 访问 CCR |
| Limits | 本地限额摘要;未配置限额时显示No limits configured |
| Edit API key | 编辑过期时间与限额;key 值本身不会再次展示 |
| Remove API key | 删除客户端访问 key,删除后立即失效 |
从 UI 实现看,列表由 packages/ui/src/pages/home/App.tsx 中的createApiKeyList(draftConfig)基于配置中的APIKEY/APIKEYS动态生成,新增与编辑分别维护apiKeyDraft与apiKeyEditDraft两份草稿状态,并通过normalizeApiKeys合并去重。
创建与编辑 API Key
创建新 key 时,界面提供以下字段:
| 字段 | 能力说明 |
|---|---|
| Name | 新 key 的显示名称,示例:Claude Code - laptop、CI或团队名 |
| Expiration | 有效期预设:Never、7 days、30 days、90 days、Custom |
| Expires at | 选择Custom时出现,设置精确的到期日期与时间 |
| API key created | 创建成功的确认对话框,展示完整 key 值 |
| Copy this key now. It may not be shown again. | 提醒立即复制 key,因为对话框关闭后 CCR 将不再展示完整值 |
两个值得注意的交互细节(均有源码佐证):
- 完整 key 只展示一次。创建后仅通过一次确认对话框给出完整值,关闭即不可见;编辑时也只能修改过期时间与限额(
Edit API key),key 值不会再次展示。这是为了降低 key 泄露面,设计上要求使用者在创建时立即保存。 - 表单校验与持久化。UI 层在 App.tsx 中校验
canSubmitApiKey:Name 必须非空;选择Custom时必须填写expiresAt。持久化走persistApiKeys→window.ccr.saveApiKeys,因此API key 的持久化仅在 Electron 应用中可用(浏览器构建会提示 "API key persistence is only available in the Electron app.")。
删除即生效
Remove API key删除的 key 会立即停止工作,无需重启网关。原因是网关在鉴权时实时读取已持久化的 key 集合(详见下文"存储与鉴权"),删除操作落盘后,下一次请求鉴权即无法再匹配到该 key。
本地限额:Advanced settings
Advanced settings为客户端 key 添加本地限额。当达到限额时,使用该 key 的请求会被拒绝或受限;提供商侧的配额不会被动用或改变——也就是说,这是网关本地的一道"护栏",与上游账户的计费配额相互独立。
| 字段 | 能力说明 |
|---|---|
| Advanced settings | 展开或收起限额编辑区 |
| No limits configured | 当前 key 未配置任何本地限额 |
| Requests | 按请求次数限流 |
| Tokens | 按 token 数量限流 |
| Images | 按图片输入数量限流 |
| per minute | 使用 1 分钟窗口 |
| per hour | 使用 1 小时窗口 |
| per day | 使用 1 天窗口 |
| Add limit | 添加一条限额规则 |
| Remove limit | 移除当前限额规则 |
限额规则与窗口的底层映射
界面上的"每分/每时/每天"对应到 窗口限流实现 中具体规则,limitRules()会将配置映射为如下规则集合:
| 配置字段 | 指标(metric) | 窗口 | 说明 |
|---|---|---|---|
windowMs+maxRequests | requests | 自定义毫秒窗口(默认 60_000ms) | 通用请求数限额 |
rpm | requests | 60_000ms(1 分钟) | 每分钟请求数 |
rph | requests | 3_600_000ms(1 小时) | 每小时请求数 |
rpd | requests | 86_400_000ms(1 天) | 每天请求数 |
tpm | tokens | 60_000ms | 每分钟 token 数 |
tph | tokens | 3_600_000ms | 每小时 token 数 |
tpd | tokens | 86_400_000ms | 每天 token 数 |
ipm | images | 60_000ms | 每分钟图片数 |
iph | images | 3_600_000ms | 每小时图片数 |
ipd | images | 86_400_000ms | 每天图片数 |
quota | tokens | quotaWindowMs(默认 86_400_000ms,即 1 天) | 按 token 的总量配额 |
对应类型定义见 packages/core/src/contracts/app.ts#L1636-L1659 中的ApiKeyLimitConfig(rpm/rph/rpd、tpm/tph/tpd、ipm/iph/ipd、maxRequests/maxTokens、windowMs/quotaWindowMs)。规则中limit <= 0或windowMs <= 0时会被直接忽略,因此未配置的指标不会产生任何拦截。
用量估算方式
网关无法预知一次请求最终产生的实际计费 token 数,因此在请求进入时基于请求体做启发式估算(estimateLimitUsage,见 window-limiter.ts):
- 仅对
POST且非空请求体估算,否则按 0 token、0 图片处理; - 输入 token ≈
ceil(输入字符数 / 4),字符统计覆盖messages、system与tools字段; - 输出 token 取
max_tokens或max_output_tokens,未提供时按 1024 兜底; - 图片计数递归统计
type为image/image_url/input_image,或携带image_url/input_image字段的输入。
超限响应
当任一条规则被触发时,网关返回429,错误体包含code: "rate_limit_exceeded"以及limit、limit_name、metric、requested、used、window_ms等明细,便于客户端理解被哪条规则拦截(见 api-key-authorizer.ts 的reserveApiKeyLimits)。计数器以"key id + 规则名 + 指标 + 窗口"为维度保存在内存中(window-limiter.ts 中的apiKeyLimitCountersMap),保留最近 2 个窗口,过期计数会自动清理,因此这是一套轻量、零外部依赖的本地限流。
存储与鉴权:源码级原理
密钥存储
API keys 持久化在 SQLite 配置库的api_keys表中(见 packages/core/src/config/config-repository.ts#L217-L235),字段包括:
id:主键;name:显示名称;encrypted_key:加密后的 key 值;encryption:加密方式标识;created_at:创建时间;expires_at:过期时间(空表示永不过期);limits_json:限额配置的 JSON 序列化。
读取通过loadPersistedApiKeys()(config-repository.ts#L201-L203)完成,写入通过replacePersistedApiKeys()完成。此外 config.ts#L664 中的saveApiKeysConfig()统一了持久化与内存配置的同步。
鉴权流程
在 api-key-authorizer.ts 的authorize()中,密钥来源有三路合并:
- 持久化的 API keys(
loadPersistedApiKeys,带 1000ms 缓存 TTL,失效后可强制refresh重新读取); - 配置中的
APIKEYS(ApiKeyConfig[]); - 旧版
APIKEY单 key 配置(兼容字段,标记为legacy)。
三者合并后会按 key 值去重。token 的提取支持Authorization头与远程控制查询参数两种方式,且 key 比较使用timingSafeEqual恒定时间比较(constantTimeEqual),避免时序侧信道泄露 key 信息——对应的测试用例也显式断言了源码中使用了node:crypto的timingSafeEqual(见 api-key-authorizer.test.mjs)。
过期判定
isApiKeyExpired()解析expiresAt并与当前时间比较;未设置expiresAt视为永不过期。界面中Never预设即对应不写入expiresAt字段。
Claude Code WIF 令牌交换
除直接鉴权外,网关还支持 Claude Code 的 WIF(Workload Identity Federation)流程:客户端向/v1/oauth/token发起urn:ietf:params:oauth:grant-type:jwt-bearer授权,网关用 API key 作为断言(assertion)校验后签发 1 小时有效期的 Bearer access_token(api-key-authorizer.ts 中exchangeClaudeCodeWifToken,expires_in恒为 3600 秒)。断言无效或过期时返回401 invalid_grant。
测试验证
packages/core/test/unit/gateway/api-key-authorizer.test.mjs 覆盖了本文涉及的核心行为,可作为功能契约参考:
- 有效 key:鉴权通过并返回对应
apiKey.id; - 无效 key:返回
401与 "Invalid API key."; - 缺失 key:返回
401与 "API key is missing."; - 过期 key:返回
401与 "API key is expired."; - WIF 交换:合法断言返回
200与access_token/expires_in/token_type,非法断言返回401 invalid_grant; - 实现约束:校验源码确实使用
timingSafeEqual做恒定时间比较。
最佳实践建议
综合界面能力与源码行为,可总结出以下实践要点:
- 按客户端/用途拆分 key:为笔记本、CI、团队分别创建独立 key,配合
Name字段标识,删除单个 key 即可精准回收某一场景的访问权限,无需重启网关。 - 短生命周期优先:临时任务使用
7 days/30 days预设,长期自动化再考虑Never或Custom;过期即401,无需人工吊销。 - 创建时立即复制:完整 key 只展示一次,务必按提示在确认对话框关闭前完成复制。
- 用本地限额兜底:为共享或自动化 key 配置 Requests / Tokens / Images 的每分钟、每小时、每天限额,防止异常流量打满网关或上游配额;注意限额是启发式估算(输入按字符数 / 4、输出默认 1024 token),并非上游精确计费。
- 删除即吊销:
Remove API key即刻生效,适合应急回收场景;删除操作与持久化存储实时联动。
适用前提与限制
- API key 的持久化保存依赖 Electron 桌面应用(
window.ccr.saveApiKeys),纯浏览器构建只能临时修改内存配置; - 本地限额为内存级窗口计数(保留最近 2 个窗口),网关重启后计数清零,适合本地单机场景的防护,不构成跨实例的分布式限流;
- token 限额基于请求体的启发式估算,实际消耗可能略有出入,配置时应预留一定余量。
【免费下载链接】claude-code-routerOne local control plane for every AI agent: route across models, fuse new capabilities, orchestrate tools, and stay fully in control.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-code-router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考