兼容 OpenAI Chat Completions API 的代理服务,支持签名校验、流式/非流式响应
本服务是一个 OpenAI API 兼容的代理中间层,位于你的 App / 客户端与真正的 LLM 服务(如 OpenAI、OpenRouter 等)之间。
┌──────────┐ 签名/鉴权 ┌──────────────┐ Bearer Token ┌──────────────┐
│ 客户端 │ ──────────────────→ │ OpenAI 代理 │ ──────────────────→ │ LLM 后端 │
│ (App) │ ←────────────────── │ (本服务) │ ←────────────────── │ (OpenAI等) │
└──────────┘ SSE 流/JSON └──────────────┘ API Response └──────────────┘
所有 API 请求(除了 /health 和 /signature-demo)都需要认证。支持以下两种方式,任选其一即可。
适用于服务端调用、快速集成场景。
Authorization: Bearer <PROXY_API_KEY>
适用于 App 端、需要防篡改和防重放的场景。需要提供三个 Header。
X-Api-Key: <PROXY_API_KEY>
X-Timestamp: <当前 Unix 时间戳(秒)>
X-Signature: <HMAC-SHA256 签名>
X-Api-Key 和 Authorization: Bearer 中的值都来自同一个环境变量 PROXY_API_KEY服务提供了一个签名示例端点,可以直接返回带正确签名的 curl 命令和 Header:
# 不需要鉴权即可访问
curl -X GET https://imseon-proxy.zeabur.app/v1/chat/completions/signature-demo
返回示例:
{
"curl_example": "curl -X POST ...",
"headers": {
"X-Api-Key": "sk-xxx",
"X-Timestamp": "1700000000",
"X-Signature": "abc123..."
}
}
以下详细说明 HMAC-SHA256 签名的计算步骤,客户端(App/服务端)需按此方式生成 X-Signature。
将整个请求体(JSON 字符串)做 SHA256 哈希,输出 hex(小写十六进制)字符串。
const bodyHash = crypto
.createHash('sha256')
.update(JSON.stringify(body))
.digest('hex');
// 输出示例: "e8b3dc2c8b9c0b6c7b8d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5"
import hashlib, json
body_hash = hashlib.sha256(
json.dumps(body, separators=(',', ':'))
).hexdigest()
# 输出示例: "e8b3dc2c8b9c0b6c7b8d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5"
# 假设 body.json 包含请求体
body_hash=$(cat body.json | sha256sum | cut -d' ' -f1)
echo "$body_hash"
按以下格式拼接,各部分用冒号 : 分隔:
"{timestamp}:{method}:{path}:{bodyHash}"
参数说明:
| 参数 | 说明 | 示例 |
|---|---|---|
timestamp |
当前 Unix 时间戳(秒),必须和请求头 X-Timestamp 一致 |
1700000000 |
method |
HTTP 方法,大写 | POST |
path |
请求路径,包含前导斜杠 | /v1/chat/completions |
bodyHash |
第 1 步计算出的请求体 SHA256 哈希 | e8b3dc2c... |
拼接后的 Payload 示例:
"1700000000:POST:/v1/chat/completions:e8b3dc2c8b9c0b6c7b8d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5"
以 PROXY_API_SECRET 为密钥,对上一步的 Payload 做 HMAC-SHA256 签名,输出 hex。
const crypto = require('crypto');
function generateSignature(secret, timestamp, method, path, body) {
const bodyHash = crypto
.createHash('sha256')
.update(body)
.digest('hex');
const payload = `${timestamp}:${method}:${path}:${bodyHash}`;
return crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
}
// 使用示例
const secret = "your-proxy-api-secret";
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = generateSignature(secret, timestamp, 'POST', '/v1/chat/completions', JSON.stringify(body));
console.log(signature);
// 输出: "a1b2c3d4e5f6..."
import hashlib
import hmac
import json
import time
def generate_signature(secret, timestamp, method, path, body):
body_hash = hashlib.sha256(body.encode('utf-8')).hexdigest()
payload = f"{timestamp}:{method}:{path}:{body_hash}"
return hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# 使用示例
body = {"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}
timestamp = str(int(time.time()))
body_str = json.dumps(body, separators=(',', ':'))
signature = generate_signature("your-proxy-api-secret", timestamp, "POST", "/v1/chat/completions", body_str)
print(signature)
# 输出: "a1b2c3d4e5f6..."
#!/bin/bash
# 完整的 curl 请求 + 自动签名
PROXY_KEY="your-api-key"
PROXY_SECRET="your-api-secret"
TIMESTAMP=$(date +%s)
BODY='{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'
BODY_HASH=$(echo -n "$BODY" | sha256sum | cut -d' ' -f1)
PAYLOAD="${TIMESTAMP}:POST:/v1/chat/completions:${BODY_HASH}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$PROXY_SECRET" | cut -d' ' -f2)
curl -X POST https://imseon-proxy.zeabur.app/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Api-Key: ${PROXY_KEY}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: ${SIGNATURE}" \
-d "$BODY"
将生成的三个 Header X-Api-Key、X-Timestamp、X-Signature 添加到 HTTP 请求中,发送到代理服务即可。
服务端收到请求后会:
X-Api-Key 是否有效无需认证。用于检查服务是否正常运行。
# 请求
curl https://imseon-proxy.zeabur.app/health
# 响应
{
"status": "ok",
"service": "openai-proxy",
"timestamp": "2025-05-24T10:00:00.000Z"
}
完全兼容 OpenAI Chat Completions API。支持流式和非流式两种模式。
| Header | 是否必须 | 说明 |
|---|---|---|
Content-Type | ✅ 是 | application/json |
Authorization 或 X-Api-Key + X-Timestamp + X-Signature | ✅ 是 | 选择一种认证方式 |
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
model |
string | 可选 | 模型名称。如果环境变量 LLM_MODEL 已设置,此值会被覆盖 |
messages |
array | 必需 | 消息列表,格式同 OpenAI。每个元素包含 role 和 content |
stream |
boolean | 可选 | 是否启用流式输出(SSE)。默认 false |
temperature |
number | 可选 | 温度,同 OpenAI 参数 |
max_tokens |
integer | 可选 | 最大生成 token 数 |
top_p |
number | 可选 | Top-p 采样 |
| 其他 OpenAI 支持的标准参数均可传递 | |||
curl -X POST https://imseon-proxy.zeabur.app/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <PROXY_API_KEY>" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"temperature": 0.7
}'
import requests
response = requests.post(
"https://imseon-proxy.zeabur.app/v1/chat/completions",
headers={
"Authorization": f"Bearer <PROXY_API_KEY>",
"Content-Type": "application/json",
},
json={
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
],
"temperature": 0.7,
}
)
print(response.json())
const response = await fetch("https://imseon-proxy.zeabur.app/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <PROXY_API_KEY>",
},
body: JSON.stringify({
model: "gpt-4o",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
],
temperature: 0.7,
}),
});
const data = await response.json();
console.log(data);
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hello! How can I help you today?" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 10,
"total_tokens": 35
}
}
# 设置 stream: true 即可开启流式 SSE 响应
curl -X POST https://imseon-proxy.zeabur.app/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <PROXY_API_KEY>" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Count from 1 to 5."}],
"stream": true
}'
import requests
from sse_starlette.sse import EventSourceResponse
with requests.post(
"https://imseon-proxy.zeabur.app/v1/chat/completions",
headers={"Authorization": "Bearer <PROXY_API_KEY>"},
json={"model": "gpt-4o", "messages": [{"role": "user", "content": "Count from 1 to 5."}], "stream": True},
stream=True,
) as r:
for line in r.iter_lines():
if line:
print(line.decode('utf-8'))
const response = await fetch("https://imseon-proxy.zeabur.app/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <PROXY_API_KEY>",
},
body: JSON.stringify({
model: "gpt-4o",
messages: [{ role: "user", content: "Count from 1 to 5." }],
stream: true,
}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
console.log(chunk);
// 每次收到 data: {...}\n\n 格式的 SSE 数据块
}
# SSE (Server-Sent Events) 格式
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1700000000,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-xxx","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
无需认证。返回当前有效的带签名字段和可直接运行的 curl 命令。
curl https://imseon-proxy.zeabur.app/v1/chat/completions/signature-demo
以下环境变量用于配置代理服务的行为。在 Zeabur 控制台设置。
| 变量名 | 必需 | 默认值 | 说明 |
|---|---|---|---|
PORT |
可选 | 3000 |
服务监听端口(Zeabur 推荐 8080) |
LLM_BASE_URL |
可选 | https://api.openai.com/v1 |
后端 LLM 服务的基础地址 |
LLM_API_KEY |
必需 | — | 后端 LLM 服务的 API Key |
LLM_MODEL |
可选 | — | 固定模型名称。设置后,客户端传入的 model 会被覆盖。 不设置则客户端可以自由指定模型 |
PROXY_API_KEY |
必需 | — | 客户端访问代理时使用的 API Key(用于 Bearer Token 和签名认证) |
PROXY_API_SECRET |
可选 | 同 PROXY_API_KEY |
HMAC-SHA256 签名的密钥。如不设置则默认为 PROXY_API_KEY |
SIGNATURE_TOLERANCE |
可选 | 300 |
签名时间戳容差(秒)。默认 5 分钟,防止重放攻击 |
| HTTP 状态码 | 错误类型 | 说明 |
|---|---|---|
401 |
未认证 | 缺少或无效的认证信息。检查 Authorization 或签名 Header 是否正确 |
401 |
签名过期 | X-Timestamp 超出容差范围(默认 ±5 分钟)。请校准客户端时钟 |
401 |
签名不匹配 | HMAC-SHA256 校验失败。检查 PROXY_API_SECRET 和签名算法实现 |
502 |
后端错误 | 代理无法连接到 LLM 后端,或后端返回了错误。检查 LLM_BASE_URL 和 LLM_API_KEY |
502 |
流式错误 | 流式请求过程中后端连接中断 |
413 |
请求体过大 | 请求体超过 10MB 限制 |
{
"error": "proxy_error",
"message": "Signature verification failed: Timestamp expired",
"status": 401
}