# 鉴权

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

来源：https://qianduan.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

访问 [qianduan.dflop.top/dashboard/keys](https://qianduan.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`

> **不知道选哪个就用 `Authorization: Bearer`。** 它是四个里唯一在**每一个**端点、
> **每一套** SDK 上都work的写法(OpenAI / Anthropic / Gemini 三套 SDK 的 base_url 指过来
> 之后都认它),本站文档的示例一律用它。下面另外三种是为「SDK 默认就发那个 header、
> 你不想改代码」准备的,照常受支持,**不是**更差的选择。

### 1. `x-api-key` Header

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

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

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

```bash
curl https://qianyi.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://qianyi.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://qianyi.dflop.top/v1/chat/completions \
  -H "Authorization: Bearer sk-gpushare-xxx" \
  ...
```

OpenAI SDK 默认走这条。

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

## 推荐做法

| 客户端 | 推荐方式 |
|---|---|
| **拿不准 / 新接入** | **`Authorization: Bearer`** —— 通用写法,所有端点都认 |
| OpenAI SDK | `Authorization: Bearer` (SDK 默认,无需改造) |
| Anthropic SDK | `x-api-key` (SDK 默认,无需改造) |
| Google Gemini SDK | `x-goog-api-key` (SDK 默认,无需改造) |
| curl / 手工 | `Authorization: Bearer` |
| 服务端代码 | env var → `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://qianyi.dflop.top/v1",
)
```

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

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

### 计费模型

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

- **体验额度**: 注册即送 **121.32**,足够跑通全部入门示例
- **充值**: 主站 dflop.top/dashboard/billing (Stripe,最低 404.4;与 qianduan.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 要求你账上有多少

**2026-09-22 起这道闸放宽了。** 此前它是 worst-case:这一笔**最坏**可能花多少,
就要求账上当场有多少 —— 于是一个余额充裕的人也可能被一笔真实成本很低的请求拒掉
(长会话 + 内联图片尤其容易命中)。现在的口径与主流平台一致:

> **要求 = `min(本笔估算成本, 该模型一轮典型请求的成本)`**

「一轮典型请求」= `min(模型 default_max_tokens, 4096) × 输出单价`,例如 GPT-6 Astra
约 24.85 积分、GPT-5.5 系约 14.91 积分。取 `min` 意味着**便宜的请求只要求它自己那点钱**
—— 你显式传一个很小的 `max_tokens` 时,要求的就是那个小数目,不会反被地板抬高。

代价是**单笔可能超支**:真实用量超过预估时按实扣,余额可以被扣成负数;
**下一次**请求会在鉴权阶段直接 402。这与 OpenAI / Anthropic 的行为一致。

> 企业组织池 (`org_id` 非空的 Key) 与托管预留额度 Key **不适用**放宽,仍按 worst-case
> 要求 —— 那两种资金的付款方不是发请求的人。

估算本身的口径:

- 输入 ≈ `(请求体字节数 − 内联媒体占的字节) ÷ 4` + 每张图 **2,000** token + 每个文件 / 音频 / 视频块 **20,000** token,最后按模型的 `context_window` 钳死
  - 内联的 base64 图片**不按它的字节长度计** —— 一张 2 MB 的图算 2,000 token,不是 50 万。四种线格式都认(OpenAI Chat 的 `image_url`、Responses 的 `input_image`、Anthropic 的 `image.source.data`、Gemini 的 `inlineData`)
  - 我们**认不出**的块形状会退回按字节计,所以自定义的私有字段里塞大 payload 可能被高估
- 输出按显式传入的 `max_tokens` **计满**;不传 `max_tokens` 时按 `min(模型 default_max_tokens, 4096)` 估算(模型真实输出天花板可以远大于此——gpt-5.x 与 Claude Opus/Sonnet 5 为 128K、GLM 与 grok 为 131072、kimi 为 256K——未传参时网关会自动替你把线上 `max_tokens` 放到模型天花板,但预估不按天花板计满,避免低余额被误拒)

余额不足以覆盖上面那个「要求」即 402。settle 阶段不做 gate,所以并发请求竞态下余额
可能变负 —— 下一次请求会被拒。

### 余额耗尽

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

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

- 余额 ≤ 0 **且套餐额度也见底**时,在鉴权阶段就直接 402(`gate=wallet`),不等 pre-charge
  - ⚠️ 「余额 0」本身**不足以**触发这道早闸:每个账号都会落在一个默认套餐上,
    套餐还有额度时早闸放行,请求走到 pre-charge,由它按「这个模型有没有被套餐覆盖」
    判定。2026-09-23 线上实测:余额 0 的新账号打 `gpt-6-astra`(不在默认套餐覆盖内),
    拿到的是 `gate=preflight` 而不是 `gate=wallet`。两者都是 402,文案里都带
    `required` 与当前余额,差别只在账本的 `gate=` 分档
- `/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 泄漏 = 完全暴露。

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