claude-code-router 网关 API 密钥完全指南:创建、过期控制与本地限额配置
CCR(claude-code-router)网关对外统一暴露了一个本地控制面入口,所有接入的客户端都需要使用 API Key 完成身份认证。本文基于官方中文文档 API 密钥 系统讲解 CCR 桌面端中 API Key 的列表管理、创建编辑与高级限额配置,并结合仓库源码深入说明密钥生成格式、鉴权判定流程、过期语义与限流计数器的底层实现。读完本文,你将能够为不同客户端(本机 Claude Code、CI、团队成员)分配相互隔离、带过期时间和本地配额限制的独立访问密钥,并在出现 401 / 403 / 429 状态时快速定位原因。
一、API Key 在 CCR 中的作用与定位
CCR 把模型路由、供应商聚合、工具编排等能力收敛为单一网关。为了控制"谁能访问这个网关",CCR 要求每个请求都携带一个由网关签发的 API Key。这个 Key 有三个核心属性:
- 身份标识:通过
名称区分客户端、团队、用途或自动化来源; - 时效约束:可设置过期时间,过期后密钥立即失效;
- 本地限额:可为单个 Key 配置请求数 / token 数 / 图片数的本地上限,防止某一路流量挤占网关整体资源。
需要特别强调的是:限额是"本地"的,命中后只会拒绝或限制使用该 Key 的请求,并不会修改供应商侧(如 Anthropic、OpenAI)的配额。换句话说,这是一种网关侧的自保护与流量治理机制,而不是供应商账单管理工具。
桌面 UI 中所有 Key 的 CRUD 逻辑集中在 API 密钥视图组件,增删改的数据最终通过管理服务接口持久化(见 management-server.ts 中的 saveApiKeys),运行配置统一存放在 SQLite 配置数据库中(默认位置见 配置数据库位置:macOS/Linux 为 ~/.claude-code-router/config.sqlite,Windows 为 %APPDATA%\claude-code-router\config.sqlite)。
二、密钥列表页:每个字段意味着什么
进入桌面端"API 密钥"页面后,主要包含以下操作与字段:
| 字段 / 操作 | 说明 |
|---|---|
| 搜索 API 密钥 | 按名称或 Key 内容过滤列表,适用于 Key 数量较多时的快速定位。 |
| 添加 API 密钥 | 打开创建弹窗,生成一个新的客户端访问 Key。 |
| 名称 | API Key 的显示名,用于区分客户端、团队、用途或自动化来源。 |
| Key | 脱敏后的访问 Key。点击 复制 API 密钥 可复制完整 Key。 |
| 过期 | 当前 Key 的过期时间。过期后客户端不能继续使用这个 Key 访问 CCR。 |
| 限制 | 当前 Key 的本地限额摘要。没有配置限额时显示"未配置限制"。 |
| 编辑 API 密钥 | 修改过期时间和限额。出于安全原因,已创建的 Key 本身不会重新明文展示。 |
| 移除 API 密钥 | 删除当前客户端访问 Key。删除后立即不能再用于请求。 |
从源码可以验证上述各列的呈现细节:
- 脱敏规则:前端 maskApiKey 对长度大于 8 的 Key 保留前 18 位并以
***结尾;长度不足则整体显示为****。因此列表页永远不会完整展示明文 Key。 - 搜索匹配:apiKeyMatchesQuery 会同时匹配名称、完整 Key、脱敏 Key、Key 的 id、过期时间与限额摘要,所以即使只记得 Key 片段也能搜到对应条目。
- 空态与提示:未配置任何 Key 时列表显示"No API keys configured",搜索无结果时显示"No matching API keys"。
列表行的操作按钮为编辑(铅笔图标)与移除(垃圾桶图标)。移除是即时生效的:后端在每次请求鉴权时动态读取持久化的 Key 集合,删除后该 Key 将不再出现在可用的鉴权候选列表中,后续携带该 Key 的请求会直接返回 401。
三、创建与编辑 API Key 的完整流程
点击"添加 API 密钥"后弹出的创建弹窗包含以下输入项:
| 字段 | 说明 |
|---|---|
| 名称 | 新 Key 的显示名。建议写成客户端或用途,例如 Claude Code - laptop、CI 或团队名。 |
| 过期时间 | 选择 Key 的有效期:永不、7 天、30 天、90 天 或 自定义。 |
| 过期于 | 选择 自定义 时出现,用于填写精确的过期日期和时间(datetime-local 选择器)。 |
| API 密钥已创建 | 创建成功后的确认弹窗,这里会显示完整 Key。 |
| 请现在复制保存这个密钥,之后可能不会再次完整显示 | 提醒你立即复制 Key。关闭弹窗后,CCR 不会再次展示完整 Key。 |
密钥是怎么生成的
从实现上看,创建流程由 createGeneratedApiKey 完成,核心逻辑包括:
- Key 值:由 generateApiKeyValue 生成,格式为
sk-前缀 + 24 字节随机数做 Base64URL 编码(无填充),整体形如sk-<约 32 个 URL 安全字符>; - Key id:
key_前缀 + 随机数,用于服务端限流计数器定位(见下文限额实现); - 随机源:优先使用
window.crypto.getRandomValues(randomBase64Url),浏览器/Electron 场景下无法调用 crypto API 时才退化到Math.random。
过期时间如何换算
过期预设在 expiresAtFromApiKeyDraft 中被换算为绝对时间戳并序列化为 ISO 8601 字符串:
永不:不写入expiresAt字段,等价于无限期;7 天/30 天/90 天:以当前时间为基准分别 +7 / +30 / +90 天;自定义:直接把用户输入的本地日期时间转换为 ISO 字符串。
创建新 Key 时默认预选"永不",自定义默认值落在创建时间 +30 天。后端在判断过期时同样解析该 ISO 字符串(见下文鉴权小节),因此前端设定与后端执行是同一份语义。
为什么编辑时看不到明文 Key
编辑弹窗只允许修改"过期时间"与"限额",而不展示、不修改 Key 本身。UI 层 updateApiKeyEditableConfig 会保留原 Key 的 id、key、name 与 createdAt,仅覆盖 expiresAt 和 limits。这是刻意的安全设计:明文 Key 只在创建成功后的确认弹窗出现一次,之后再无读取路径,从而降低密钥被截获、泄露的风险。
四、高级设置:给单个客户端添加本地限额
"高级设置"用于给单个客户端 Key 添加本地限额。限额命中后,使用这个 Key 的客户端请求会被拒绝或限制;它不会修改供应商侧额度。
| 字段 | 说明 |
|---|---|
| 高级设置 | 展开或收起限额编辑区。 |
| 未配置限制 | 当前 Key 没有任何本地限额。 |
| 请求 | 按请求次数限制。 |
| 令牌 | 按 token 数量限制。 |
| 图片 | 按图片数量限制。 |
| 每分钟 | 限额窗口为 1 分钟。 |
| 每小时 | 限额窗口为 1 小时。 |
| 每天 | 限额窗口为 1 天。 |
| 添加限制 | 新增一条限额规则。 |
| 移除限制 | 删除当前限额规则。 |
在 UI 上,每条限额规则 =「指标(请求 / 令牌 / 图片)+ 窗口(每分钟 / 每小时 / 每天)+ 阈值数值」。你可以为同一个 Key 叠加多条规则(例如每分钟 60 次请求 + 每天 10 万 token),任意一条命中即触发限流。编辑已存在的 Key 时高级设置默认展开(defaultOpen),便于快速查看和修改既有限额。
限制在配置中的字段映射
前端会把"指标 × 窗口"组合转换为底层配置字段。映射函数 apiKeyLimitField 定义如下:
| 指标 \ 窗口 | 每分钟 | 每小时 | 每天 |
|---|---|---|---|
| 请求 | rpm |
rph |
rpd |
| 令牌 | tpm |
tph |
tpd |
| 图片 | ipm |
iph |
ipd |
配置在加载 / 展示时会走另一侧的归一化逻辑:apiKeyLimitRowsFromConfig 把已有的 rpm/tph 等字段反向还原成编辑表单的行,另有 maxRequests + windowMs、maxTokens + quotaWindowMs 这类可自定义窗口的兼容字段(如回落到分钟/天窗口)。底层归一化见 normalizeApiKeyLimits,所有数值必须为正整数才会被采纳。
五、底层原理:鉴权与限额是如何被执行的
理解了 UI 语义之后,再来看网关侧的执行逻辑,可以帮你预判各种状态码的来源。
鉴权判定流程
网关对所有请求统一走 authorize,流程如下:
- 读取可用 Key 集合;若当前没有任何可用 Key,直接返回
403(提示先保存一个网关 Key 或重启 CCR 自动生成); - 从请求中提取 token:优先读取请求头,支持
Authorization: Bearer <key>与x-api-key: <key>两种形式(见 readAuthToken,Bearer前缀会被自动剥离);对于远程控制类接口,还会从查询参数api_key/key读取(readRemoteControlQueryAuthToken); - 在 Key 集合中查找匹配项;匹配使用常数时间比较
timingSafeEqual(constantTimeEqual),避免通过响应耗时侧信道探测密钥; - 若找到但已过期,返回
401,错误信息为API key is expired.; - 未找到:带 token 返回
401 Invalid API key.,完全没带 token 返回401 API key is missing.。
因此你可以用状态码快速诊断:403 = 网关还没有任何 Key;401 + expired = Key 已过期;401 + invalid/missing = Key 不对或没带。
过期语义
过期判断 isApiKeyExpired 非常简单:若 Key 配置中没有 expiresAt 则不视为过期(即"永不");否则解析该 ISO 时间字符串并与当前时间比较,expiresAt <= now 即判为过期。所以创建 Key 时设置的"过期于"是精确到秒的硬性截止时刻,过期后无需任何额外操作,该 Key 立即可用性消失。
另外 CCR 还支持 Claude Code 的 OAuth/WIF 交换路径 /v1/oauth/token(exchangeClaudeCodeWifToken):Claude Code 以 jwt-bearer 作为 grant type,把网关 API Key 当作断言(assertion)换取临时 access_token(有效期 3600 秒),换取时同样会做匹配与过期校验。这就是"用 API Key 认证 Claude Code 客户端"背后的标准协议实现。
限额如何计数与触发
在请求鉴权通过之后,网关会调用 reserveApiKeyLimits 预占限额。真正的窗口与规则逻辑在 window-limiter.ts:
- 规则展开:limitRules 会把配置展开为一组规则。默认窗口换算为毫秒:每分钟 =
60_000、每小时 =3_600_000、每天 =86_400_000; - 计数键:每个窗口计数器以
api-key|<key id>|<规则名>|<指标>|<窗口毫秒>|<窗口起点>为键,天然把不同 Key、不同指标、不同窗口的用量隔离开(reserveApiKeyLimits); - 命中即拒:任意一条规则上"已用量 + 本次请求量 > 限额"时,请求返回
429,错误码为rate_limit_exceeded,响应体附带limit、metric、requested、used、window_ms等明细,方便你判断到底是哪条规则被触发; - 先检查后扣减:所有规则全部放行后才真正累加计数,避免部分规则占用却最终失败产生"假消耗";
- 内存窗口:计数器保留最近 2 个窗口并定期清理过期窗口(readWindowCounter),也就是说限额统计是进程内的、面向短时间窗口的本地治理。
token 与图片是如何"估算"的
"请求数"按 1 次请求 = 1 计数,而 token 与图片数则由 estimateLimitUsage 对请求体估算:
- 只有
POST且非空请求体会被统计,GET 等请求不消耗 token / 图片额度; - token 估算:输入部分取
messages、system、tools内容的字符数总和,按约 4 字符 ≈ 1 token 折算;输出部分读取max_tokens/max_output_tokens,缺省按 1024 计;最终总量下限为 1; - 图片估算:递归遍历请求体,识别
image、image_url、input_image类型的 content 块及其变体字段,每出现一张图片计 1(countImageInputs)。
需要说明的是,这是网关在转发前做的近似估算(用于本地限额),并非供应商账单上的精确 token 计量,因此与最终计费数值可能存在差异。
六、最佳实践建议
- 一 Key 一用途:名称建议直接携带客户端与环境信息(如
Claude Code - laptop、CI),列表按名称搜索即可快速定位问题来源; - 短期密钥及时轮换:临时授权或共享给外部协作者时优先选择
7 天/30 天,避免出现永不过期的遗留密钥;需要精确截止时使用"自定义"; - 创建后立即复制:明文 Key 只在"API 密钥已创建"弹窗展示一次,务必当场保存到密码管理器,之后只能复制(前端仅保留脱敏值),无法重新查看;
- 限额从请求维度起步:先配置"每分钟请求数"这类粗粒度规则防止突发流量,再视情况叠加每日 token 上限;删除旧 Key 前确认没有正在运行的客户端仍在使用它,因为移除后该 Key 会立即失效;
- 结合状态码排查:
403检查网关是否初始化了 Key,401核对 Key 是否过期/正确及请求头格式(Authorization: Bearer或x-api-key),429则解析错误响应体中的limit_name/metric/window_ms找到被触发的限额规则。
相关实现与配置入口汇总:UI 界面 components/api-keys.tsx、Key 生成/换算/脱敏 shared/api-keys.ts、鉴权与 WIF auth/api-key-authorizer.ts、限额窗口计数 limits/window-limiter.ts、请求头读取 http/io.ts、Key 持久化 management-server.ts。如需在 Claude Code 等客户端中使用这些 Key 连接 CCR,可进一步参考中文文档中的 服务端/连接配置 系列页面。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00