跳到主要内容

认证与签名

直接 HTTP OpenAPI 的调用分两步:签名申请 Token,再用 Token 与当前业务请求签名调用接口。Access Secret 是签名密钥;OAuth 客户端的 client_secret 是另一项配置,两者不能互相替代。

1. 生成签名​

产品文档的 生成签名 页面提供 JSON 参数输入、Token 示例、JSON 示例及代码导出,可选择 JavaScript、Java、PHP、Python、C#、Go、Android 或 iOS。

通用步骤:

  1. 取本次实际发送的签名参数,排除 signature 自身,不提交无意义的空参数。
  2. 对象、数组转换成 JSON 字符串;标量转换为字符串。
  3. 按参数名 ASCII 升序排列,拼接 key1=value1&key2=value2。
  4. 使用 Access Secret 对拼接字符串计算 HMAC-SHA256。
  5. 输出小写十六进制,填入本次请求的 signature。

选择服务端使用的语言,点击代码块右上角的复制按钮即可复制当前示例。各示例输入均为参数名 → 最终字符串值:请先按上面的规则序列化对象、数组和标量,签名与实际发送使用同一份字符串,不再交给不同语言重新序列化。

Node.js · HMAC-SHA256
import {createHmac} from 'node:crypto';

function sign(params, accessSecret) {
const entries = Object.entries(params)
.filter(([key, value]) => key !== 'signature' && value != null)
.filter(([, value]) => value.trim() !== '')
.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
const message = entries.map(([key, value]) => `${key}=${value}`).join('&');
return createHmac('sha256', accessSecret).update(message, 'utf8').digest('hex');
}

参数名使用接口规定的 ASCII 名称;空值应先从最终请求中去掉,值为 "0"、"false" 时应保留。用于过滤空白的判断不会修改非空参数的原值。Java 示例要求 Java 17 或以上;其余示例分别使用 Node.js、Python 3、PHP 和 Go 标准库。上方代码只在服务端运行,不把 Secret 填入网页。

先构建最终 payload,再生成签名。签名后不要修改字段值、对象序列化顺序或时间戳;不能先对 URL 编码后的整段文本签名。Token 参数与业务参数分别生成,不能复用上一次签名。

网关会合并指定 Header、query、body 中的签名参数。重复提供同名参数时必须完全一致,不要在 Header 中保留旧的 app_id 或 timestamp。不要将 Access Secret 放进公开网页、App 包或发送给业务接口。

2. 申请 Token​

接口为 POST /auth/oauth2/token,使用 application/x-www-form-urlencoded。普通业务接口的 JSON 要求不能套用到这个请求。

位置参数说明
HeaderTENANT-ID数字租户 ID
HeaderContent-Typeapplication/x-www-form-urlencoded
表单grant_type固定 openapi
表单username获授权的用户名
表单app_id应用 ID
表单timestamp当前毫秒时间戳
表单client_id、client_secret当前部署的认证客户端配置
表单access_id当前授权的访问 ID,提供时按它匹配密钥
表单signature上述实际表单参数的签名

示例接续上面的 sign 函数,环境变量由调用服务注入:

const params = {
grant_type: 'openapi',
username: process.env.DUOGEX_USERNAME,
app_id: process.env.DUOGEX_APPLICATION_ID,
timestamp: String(Date.now()),
client_id: process.env.DUOGEX_CLIENT_ID,
client_secret: process.env.DUOGEX_CLIENT_SECRET,
access_id: process.env.DUOGEX_ACCESS_ID,
};
const body = new URLSearchParams({
...params,
signature: sign(params, process.env.DUOGEX_ACCESS_SECRET),
});
const response = await fetch(`${process.env.DUOGEX_API_BASE}/auth/oauth2/token`, {
method: 'POST',
headers: {
'TENANT-ID': process.env.DUOGEX_TENANT_ID,
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
});
const result = await response.json();
if (!response.ok || !result.access_token) {
throw new Error('获取 Token 失败,请核对业务错误与授权配置');
}
// 将令牌保存在服务端,按实际 expires_in 管理有效期。

运行前必须配置示例中的全部环境变量。页面有历史客户端默认值,正式接入应使用管理员确认的当前配置,不将默认值当作已授权凭据。页面响应示例为 43200 秒,有效期仍按真实响应管理。

3. 调用业务接口​

读取列表等签名业务接口携带以下请求头:

Authorization: Bearer YOUR_ACCESS_TOKEN
TENANT-ID: YOUR_NUMERIC_TENANT_ID
X-APPLICATION-ID: YOUR_APPLICATION_ID
app_id: YOUR_APPLICATION_ID
timestamp: CURRENT_TIMESTAMP_IN_MILLISECONDS
signature: SIGNATURE_FOR_THIS_REQUEST
Content-Type: application/json

签名参数包含当前业务 payload、app_id、timestamp,以及实际提供的 access_id。signature 不参与自身计算。不要把申请 Token 时的 grant_type、client_secret 等无关参数复制到业务请求。

HTTP 认证、业务签名与表单权限分别校验。签名正确不代表用户有权读取或修改全部记录。下一步见数据查询。