Skip to content

认证与签名

OpenAPI 模块的所有接口都需要 MD5 token。每个请求必须带三个 HTTP 请求头。

必需请求头

请求头说明
x-auth-nameWMG 分配的 API 客户端名称
x-auth-seed13 位毫秒级 UNIX 时间戳(如 1746700000000
x-auth-tokenMD5 认证 token(公式见下)

seed

平台时钟运行在新加坡时间(UTC+08:00)x-auth-seed 的时间窗按该时钟校验。

规则

  • 13 位数字 UNIX 时间戳,单位是毫秒 —— 不是秒,也不是格式化后的日期字符串。
  • 必须与平台时间相差不超过 ±10 分钟,否则请求会被拒绝并返回 x-auth-seed: TIMEOUT
  • 在发起请求前一刻生成,不要在脚本启动时生成一次后复用。

UNIX 时间戳表示的是一个绝对时刻,因此只要你的系统时钟准确、客户端换算正确,无论机器设置成新加坡时区还是其他时区,得到的数值都相同。下表中的每种写法都能得到与新加坡平台时间一致的值。

x-auth-seed新加坡时间(UTC+08:00)
17467000000002025-05-08 18:26:40 +08:00
17537530690002025-07-29 09:37:49 +08:00

各语言生成 seed

语言写法
BashSEED=$(date +%s%3N)
PowerShell$SEED = [string]([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())
Pythonseed = str(int(time.time() * 1000))
Node.jsconst seed = String(Date.now());
PHP$SEED = (string)(time() * 1000);

PowerShell:禁止使用 Get-Date -UFormat %s

这个写法返回的是 10 位秒级值,会被拒绝并返回 x-auth-seed: LENGTH MUST BE 13 DIGITS。在 Windows PowerShell 5.1 下它还是按本地时间换算的,机器设为新加坡时区时会超前 28800 秒(8 小时)

powershell
# Windows PowerShell 5.1,机器时区为 Singapore Standard Time
Get-Date -UFormat %s                                 # 1785149525     <- 秒级,且超前 8 小时
[DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()    # 1785120724000  <- 正确

PowerShell 7 下时刻是正确的,所以时钟问题只有在同一份脚本跑到 5.1 上时才暴露;但位数在两个版本下都是错的。请一律使用 [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()

Token 公式

x-auth-token = md5( strtolower(api_name) + api_key + seed )

结果为 32 位小写十六进制字符串。请求参数不参与 token 计算。

分步说明

  1. API name 转小写 —— 把 x-auth-name 转为小写。
  2. 拼接 小写后的名称、你的 api_keyseed 字符串 —— 中间不加任何分隔符。
  3. MD5 哈希 对拼接后字符串的 UTF-8 字节做 MD5。
  4. 十六进制编码 把原始二进制哈希转成 32 位小写十六进制字符串。
  5. 把结果放入 x-auth-token 请求头。

注意: 拼接时 seed 必须是字符串,不要先转成整数。MD5 的输入是拼接后字符串的原始 UTF-8 字节。

参考实现

Bash

bash
API_NAME="your_api_name"
API_KEY="your_api_key"
SEED=$(date +%s%3N)
TOKEN=$(printf '%s' "$(echo -n "$API_NAME" | tr '[:upper:]' '[:lower:]')${API_KEY}${SEED}" | md5sum | cut -d' ' -f1)

curl -G "https://api.test.wmgdelivery.com/v1/openapi/station/get-stations" \
  -H "x-auth-name: $API_NAME" \
  -H "x-auth-seed: $SEED" \
  -H "x-auth-token: $TOKEN"

Windows PowerShell

powershell
$API_NAME = "your_api_name"
$API_KEY  = "your_api_key"
$SEED     = [string]([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())

$raw   = [System.Text.Encoding]::UTF8.GetBytes($API_NAME.ToLower() + $API_KEY + $SEED)
$md5   = [System.Security.Cryptography.MD5]::Create().ComputeHash($raw)
$TOKEN = -join ($md5 | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "x-auth-name"  = $API_NAME
    "x-auth-seed"  = $SEED
    "x-auth-token" = $TOKEN
}

$response = Invoke-RestMethod -Uri "https://api.test.wmgdelivery.com/v1/openapi/station/get-stations" -Method Get -Headers $headers
$response | ConvertTo-Json -Depth 10

Python

python
import time, hashlib
import requests

API_NAME = 'your_api_name'
API_KEY  = 'your_api_key'


def build_token(api_name: str, api_key: str, seed: str) -> str:
    """seed 必须是毫秒时间戳的字符串形式 —— 不要转成 int。"""
    raw = api_name.lower() + api_key + seed
    return hashlib.md5(raw.encode()).hexdigest()


seed  = str(int(time.time() * 1000))
token = build_token(API_NAME, API_KEY, seed)

resp = requests.get(
    'https://api.test.wmgdelivery.com/v1/openapi/station/get-stations',
    headers={
        'x-auth-name':  API_NAME,
        'x-auth-seed':  seed,
        'x-auth-token': token,
    },
)
print(resp.json())

Node.js / TypeScript

仅使用内置的 node:crypto 模块和全局 fetch(Node.js 18+)。

typescript
import crypto from 'node:crypto';

const API_NAME = 'your_api_name';
const API_KEY  = 'your_api_key';

function buildToken(apiName: string, apiKey: string, seed: string): string {
  // seed 必须保持字符串;拼接后按原始 UTF-8 字节计算
  return crypto.createHash('md5')
    .update(apiName.toLowerCase() + apiKey + seed)
    .digest('hex');
}

const seed  = String(Date.now());
const token = buildToken(API_NAME, API_KEY, seed);

const resp = await fetch('https://api.test.wmgdelivery.com/v1/openapi/station/get-stations', {
  headers: {
    'x-auth-name':  API_NAME,
    'x-auth-seed':  seed,
    'x-auth-token': token,
  },
});
console.log(JSON.stringify(await resp.json(), null, 2));

PHP

php
<?php

function build_token(string $api_name, string $api_key, string $seed): string
{
    return md5(strtolower($api_name) . $api_key . $seed);
}

$API_NAME = 'your_api_name';
$API_KEY  = 'your_api_key';
$SEED     = (string)(time() * 1000);   // 13 位毫秒时间戳
$TOKEN    = build_token($API_NAME, $API_KEY, $SEED);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.test.wmgdelivery.com/v1/openapi/station/get-stations');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'x-auth-name: '  . $API_NAME,
    'x-auth-seed: '  . $SEED,
    'x-auth-token: ' . $TOKEN,
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
curl_close($ch);
echo json_encode(json_decode($response, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
?>

验证样例

输入

API 凭证:

api_name = "Demo_Client"
api_key  = "abc"

seed(毫秒时间戳):

seed = "1746700000000"

计算

php
$token = build_token('Demo_Client', 'abc', '1746700000000');

结果

拼接后的字符串:

demo_clientabc1746700000000

MD5 结果:

6e7190914323a4b7e2f94ba798a3f015

这个结果是可复现的 —— 把同样这三个输入(Demo_Clientabc1746700000000)交给任何 MD5 实现,都必然得到这个值。正式对接前请先用它验证你的实现。

样例分步拆解

1. API name 转小写

"Demo_Client" → "demo_client"

2. 拼接

"demo_client" + "abc" + "1746700000000"
= "demo_clientabc1746700000000"

3. MD5 哈希

对拼接后字符串的 UTF-8 字节计算 MD5(二进制输出),再转十六进制:

md5("demo_clientabc1746700000000") → 6e7190914323a4b7e2f94ba798a3f015

在请求中使用

token 生成后,按如下方式带入请求:

  1. 把 API name 放入 x-auth-name 请求头
  2. 把 seed 放入 x-auth-seed 请求头
  3. 把计算出的 token 放入 x-auth-token 请求头

示例:

x-auth-name:  Demo_Client
x-auth-seed:  1746700000000
x-auth-token: 6e7190914323a4b7e2f94ba798a3f015

排错

现象常见原因
x-auth-token: INVALID拼接前没有把 API name 转小写;用错了 key 或 seed
x-auth-seed: TIMEOUTseed 在脚本启动时生成后复用了,没有在发请求前一刻生成;客户端时钟漂移;或在 Windows PowerShell 5.1 上用了 Get-Date -UFormat %s(见 seed
x-auth-seed: LENGTH MUST BE 13 DIGITS传了秒级时间戳(10 位)而不是毫秒 —— 需要乘以 1000,见 seed

认证错误消息

错误消息原因
x-auth-seed: FORMAT ERRORseed 不是数字
x-auth-seed: LENGTH MUST BE 13 DIGITSseed 不是恰好 13 位
x-auth-seed: TIMEOUTseed 与平台时间相差超过 10 分钟
x-auth-name: INVALIDapi_name 不存在或账号已停用
x-auth-token: INVALIDtoken 不匹配

标准响应结构

所有接口都返回如下结构的 JSON:

json
{
  "code": 0,
  "message": "success",
  "data": {}
}
字段类型说明
codeinteger0 = 成功,1 = 失败
messagestring可读的结果描述
dataobject/array响应数据;出错时为 []

未预期的服务端异常同样返回 code: 1messagesomething error