认证
两种认证头、API Key 格式,以及 401 的排查路径。
所有端点都需要认证,包括 /v1/models。
两种写法
网关同时接受下面两种头,在所有端点上都有效——包括 OpenAI 端点也接受
x-api-key,Anthropic 端点也接受 Authorization。这正是同一个 Key 能同时喂给两家
SDK 的原因。
# 写法一:OpenAI 惯例
curl https://api.jucode.cn/v1/models \
-H "Authorization: Bearer sk-juc-..."
# 写法二:Anthropic 惯例
curl https://api.jucode.cn/v1/models \
-H "x-api-key: sk-juc-..."只有当 Authorization 头缺失或格式无效时,网关才会去读 x-api-key。两个都传时,
Authorization 优先。
Bearer 前缀大小写敏感
必须是恰好 Bearer ——首字母大写,后跟一个空格。
bearer sk-juc-...、BEARER sk-juc-...、或用制表符分隔,都会被判定为空凭证,
返回 401 invalid_api_key 且提示 missing credentials。这个报错看起来像"没传
Key",实际是"Key 传了但前缀写错了",容易误导排查方向。
浏览器端的限制
如果你从浏览器直接调用网关,有两条限制:
必须用 Authorization,不能用 x-api-key。 网关的 CORS 允许头列表里没有
x-api-key,跨域预检会失败。
读不到响应头。 网关没有设置 Access-Control-Expose-Headers,所以浏览器 JS
拿不到 X-Request-Id、Retry-After、X-RateLimit-*。排查问题时如果需要
request_id,只能从响应体的 JSON 里取——错误响应体里有这个字段。
更根本的问题:在浏览器里直接调用意味着 API Key 会暴露给终端用户。生产环境请在你 自己的后端中转,不要把 Key 下发到前端。
API Key 格式
sk-juc-<43 位 base64url 字符>总长 50 位,正则是 ^sk-juc-[A-Za-z0-9_-]{43}$。控制台列表里显示的"前缀"是
sk-juc- 加前 8 位,用来辨认是哪一把 Key,不是完整凭证。
完整明文只在创建那一刻返回一次。丢了就只能重新创建。
OAuth 设备令牌
除 API Key 外,网关也接受 OAuth 设备访问令牌(CLI、桌面端登录后拿到的那种)。
把它当作 Bearer 凭证传即可,网关按前缀区分:sk-juc- 开头走 API Key 路径,
其余一律当 JWT 解析。
用 OAuth 令牌调用时有两点固定行为,不受控制台配置影响:
- 总是自动路由,不受 API Key 的模型组白名单限制
- 总是按个人额度计费,不走组织账单
另外,普通网页会话令牌不能用于调用 API——只有 oauth_access 类型的令牌有效。
401 排查
网关没读到任何凭证。按可能性排序:
Bearer前缀写错了大小写或空格(最常见,见上文)- 请求头名字拼错
- 某些 HTTP 客户端在跟随重定向时会丢弃
Authorization头
Key 不存在或已被吊销。确认没有多余的空格或换行——从控制台复制时容易带上尾部换行。
凭证不以 sk-juc- 开头,被当作 OAuth JWT 解析但没通过。
注意 OAuth 令牌过期和无效返回的是同一个错误码,无法从响应区分。如果你在用 CLI 或桌面端,先尝试重新登录。
只有 API Key 的过期有独立错误码(见下条)。
API Key 过了设定的有效期。到控制台新建一把,或延长现有 Key 的有效期。
403 而非 401
下面两种情况凭证本身有效,但账号状态不允许调用,返回 403:
| code | 含义 |
|---|---|
account_disabled | 账号非活跃状态 |
account_frozen | 因违规被临时冻结,过期后自动恢复 |
注意这两个的 error.type 仍然是 authentication_error,不是 permission_error
——所以别用 type 字段来区分该不该重试,要看 code。
Anthropic 端点的 401 格式不同
/anthropic/v1/* 的认证失败返回的是 OpenAI 格式的错误体,不是 Anthropic 格式:
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "missing credentials",
"request_id": "..."
},
"request_id": "..."
}原因是认证在进入 Anthropic 处理器之前就完成了,走的是共用的中间件。
实际影响:Anthropic 官方 SDK 的错误解析可能无法识别这个响应体,你看到的可能是一个
泛化的异常而非 AuthenticationError。认证配好之后就不会再遇到,但首次接入调试时
值得知道。