# 鉴权

> API Key 格式 + 4 种鉴权方式 + Key 管理 + 余额与计费

来源：https://zhonkemodel.dflop.top/docs/reference/authentication

## API Key 格式

API Key(控制台中显示为「子密钥」)格式:

```
sk-gpushare-{64 字符十六进制}
```

例: `sk-gpushare-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`

总长度 76 字符(`sk-gpushare-` 前缀 12 字符 + 64 位十六进制)。写 Key 校验 / secret-scanning 正则时按这个长度配置。

原始 Key 服务端以 AES-256-GCM 加密存储,**可以在控制台 Key 详情页随时重新查看**(reveal)。只有早期创建的少数 hash-only 老 Key 不支持 reveal——丢了就重建一把。

同一把 Key 通用于全部对外端点: 四个聊天协议端点、[图像 / 视频 / 音乐 API](./media-apis.md)、[知识库 API & MCP](./wiki-api.md)。

## 创建 / 管理 Key

访问 [zhonkemodel.dflop.top/dashboard/keys](https://zhonkezhonkemodel.dflop.top/dashboard/keys):

1. **Create Key** —— 命名(必填) + 可选 `allowed_models` 白名单 + 可选过期时间
2. **随时查看** —— Key 详情页可 reveal 原始 Key(服务端加密存储)
3. **查看用量** —— 历史调用日志(model / token / 费用逐条可查)
4. **撤销** —— 任何 Key 可立即 disable 或删除

### Key 的可配置项

| 项 | 含义 | 默认 |
|---|---|---|
| `name` | 可读名称,审计用 | **必填** |
| `allowed_models` | 模型白名单;`NULL` / 不传 = 允许全部模型。⚠️ **不要传空数组** —— 空数组 = 拒绝所有模型,每个请求都会返回 400 `model_not_allowed` | 不传 (允许全部) |
| `expires_at` | 过期时间 | 无 |
| `enabled` | 启用 / 禁用 | true |

> **Key 上没有预算配置。** 预算在账户余额(所有 Key 共享,见下文 [余额与计费](#余额与计费-统一钱包))。Key 级的隔离手段只有 `allowed_models` / `expires_at` / `enabled`,用于权限隔离与审计,不是预算隔离。

## 4 种鉴权方式

Gateway 按以下优先级查找 Key,**任一种生效**(命中即停):

`x-api-key` → `x-goog-api-key` → `?key=` → `Authorization: Bearer`

### 1. `x-api-key` Header (推荐)

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/messages \
  -H "x-api-key: sk-gpushare-xxx" \
  ...
```

Anthropic SDK / Claude Code 默认走这条。最直白,也最不易被中间层吞掉。

### 2. `x-goog-api-key` Header

```bash
curl https://zhonkezhonkeapi.dflop.top/v1beta/models/gemini-2.5-flash:generateContent \
  -H "x-goog-api-key: sk-gpushare-xxx" \
  ...
```

Google `genai` SDK 的默认 header —— Gemini SDK 用户**无需任何改造**直接可用,且 header 值不会进 URL access log,没有泄漏问题。

### 3. `?key=` Query Param

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1beta/models/gemini-2.5-flash:generateContent?key=sk-gpushare-xxx" \
  ...
```

Gemini REST 风格的替代写法。**不推荐手写** —— Key 会出现在 access log / browser history,有泄漏风险。

### 4. `Authorization: Bearer` Header

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/chat/completions \
  -H "Authorization: Bearer sk-gpushare-xxx" \
  ...
```

OpenAI SDK 默认走这条。

> **例外**: `GET /v1/models` 只接受 `Authorization: Bearer` 和 `x-api-key` 两种 header,**不支持 `?key=`**。

## 推荐做法

| 客户端 | 推荐方式 |
|---|---|
| OpenAI SDK | `Authorization: Bearer` (SDK 默认) |
| Anthropic SDK | `x-api-key` (SDK 默认) |
| Google Gemini SDK | `x-goog-api-key` (SDK 默认即可,无需改造) |
| curl / 手工 | `x-api-key` |
| 服务端代码 | env var → `x-api-key` 或 `Authorization: Bearer` |

### 用 env var 不要硬编码

```bash
# ~/.bashrc
export PLATFORM_API_KEY=sk-gpushare-xxx
```

```python
client = OpenAI(
    api_key=os.environ["PLATFORM_API_KEY"],
    base_url="https://zhonkezhonkeapi.dflop.top/v1",
)
```

## 余额与计费(统一钱包)

本平台采用 **预付费账户钱包**,不是 post-pay。

### 计费模型

预算在**账户余额**(USD 钱包),**所有 API Key 共享同一余额**。Key 本身没有独立预算池——Key 只是访问凭证。

- **体验额度**: 注册即送 **121.32**,足够跑通全部入门示例
- **充值**: 主站 dflop.top/dashboard/billing (Stripe,最低 404.4;与 zhonkemodel.dflop.top 同账号 SSO,共享同一余额)
- **查询**: 控制台 dashboard 实时显示余额与各 Key 用量明细

计费单位按端点类型:

| 端点 | 计费方式 |
|---|---|
| chat / embeddings | 按 token (真实用量 × 单价;上游返回 `cached_tokens` 时自动按 cached 价计费,无需显式开启) |
| 图像 | 按张 (单价 × 张数,详见 [媒体 API](./media-apis.md)) |
| 视频 | 按秒 (按实际生成时长结算,失败全额退回) |

每次 chat 请求 gateway 做两步:

1. **Pre-charge** —— 按最坏情况估算成本,跟账户余额比对,超了返回 402,不发上游
2. **Settle** —— turn 结束后按真实 token 用量扣减账户余额,并写入用量日志

### Pre-charge 如何估算

Pre-charge 用 worst-case 口径:

- 输入 ≈ messages JSON 字节数 ÷ 4
- 输出按显式传入的 `max_tokens` **计满**;不传 `max_tokens` 时按 `min(模型 default_max_tokens, 32768)` 估算(模型真实输出天花板可以远大于此——gpt-5.x 与 Claude Opus/Sonnet 5 为 128K、GLM 与 grok 为 131072、kimi 为 256K——未传参时网关会自动替你把线上 `max_tokens` 放到模型天花板,但预估不按天花板计满,避免低余额被误拒)

估算值 > 余额即 402,**即使真实用量远小于余额**。余额紧张时显式传一个较小的 `max_tokens` 即可通过 pre-charge。另外 settle 阶段不再做 gate,并发请求竞态下余额可能轻微变负(下次请求会被拒)。

### 余额耗尽

返回 HTTP **402 Payment Required**。OpenAI 形状 (`/v1/chat/completions` 等):

```json
{"error": {"message": "余额不足，请充值后重试。", "type": "insufficient_quota", "code": "quota_exceeded"}}
```

- 余额 ≤ 0 时**在鉴权阶段就直接 402**,不会等到 pre-charge
- `/v1/messages` 返回 Anthropic 形状 (`type: "billing_error"`,无 `code` 字段),`/v1beta` 返回 Gemini 形状 (`status: "RESOURCE_EXHAUSTED"`),三协议对照见 [错误处理](./errors.md)

**修复**: 到 dflop.top/dashboard/billing 充值。**新建 Key 不能解决额度问题** —— 所有 Key 共享同一余额,余额耗尽时所有 Key 同时 402。

## 多 Key 策略

Key 是凭证不是预算池,多把 Key 的价值在**权限隔离 + 审计归因**:

| 场景 | Key 配置建议 |
|---|---|
| 个人开发 / 试玩 | 一把不限模型的 Key |
| 生产服务 | `allowed_models` 锁定 1-2 个 model,独立命名便于审计 |
| 临时调用 / spike | 设 `expires_at` 的短期 Key,跑完撤销 |
| 团队协作 | 每人一把,按用户名命名 (注意: 共享同一账户余额) |

## 安全建议

1. **永远不要** 把 Key 写进 git 仓库 / 截图 / Slack 消息
2. 用 [direnv](https://direnv.net/) / 1Password CLI / Doppler 注入 env var
3. 怀疑泄漏立即在控制台撤销
4. 服务端代码用环境变量,不要从前端发起调用 (会暴露 Key)
5. 浏览器端必须调用时,做后端代理转发
6. Key 可在控制台随时 reveal,意味着**控制台账号本身的安全同样关键** —— 保护好 SSO 账号

## CSRF / Origin

本网关 endpoints **不强制 Origin / CSRF token** —— 全靠 Key 鉴权。所以 Key 泄漏 = 完全暴露。

(zhonkemodel.dflop.top 控制台**自身**用 cookie + CSRF token,但那是单独的管理面板鉴权,不影响 gateway API)
