鉴权签名
每次请求都必须携带以下请求头:
| 请求头 | 必填 | 说明 |
|---|---|---|
X-API-Key |
是 | 企业后台 API Key 管理中生成的 Key。 |
X-Timestamp |
是 | Unix 秒级时间戳,允许与服务端有 5 分钟内时钟偏差。 |
X-Nonce |
是 | 每次请求唯一随机串,用于防重放。推荐直接使用 UUID/GUID 等高强度随机值,禁止使用固定前缀 + 秒级时间戳这类易重复方案。 |
X-Signature |
是 | 按本文档规范拼接原文后,用 API Secret 做 HMAC-SHA256,结果转 16 进制小写。 |
X-Employee-Virtual-Id |
按接口要求 | 当前访问员工的 virtualId。只有需要识别当前员工身份的接口才必须传。 |
签名原文格式
METHOD PATH_LOWERCASE QUERY_STRING API_KEY TIMESTAMP NONCE EMPLOYEE_VIRTUAL_ID BODY
拼接规则:
METHOD必须转为大写,例如GET、POST。PATH必须使用实际请求路径,并统一转为小写,例如/api/openplatform/employees。QUERY_STRING必须与实际请求 URL 完全一致;有查询串时必须包含前导?,没有时传空字符串。重要:如果查询参数包含中文或特殊字符,必须先进行 URL 编码(例如Uri.EscapeDataString或encodeURIComponent),签名原文中的查询串必须与实际发送的 URL 中的查询串完全一致。- 如果本次请求带了
X-Employee-Virtual-Id,则EMPLOYEE_VIRTUAL_ID必须传该请求头的原始值;没有该请求头时传空字符串。 BODY必须与实际发送的请求体完全一致;GET 请求 body 为空字符串;multipart/form-data上传附件时固定传[multipart/form-data]。- 签名原文内部统一使用
\n作为换行符,不使用\r\n。 X-Nonce必须为每次 HTTP 请求重新生成的唯一随机串;同一个 API Key 下,已通过鉴权的X-Nonce不可再次使用。- 如果客户端需要重试请求,必须同时重新生成
X-Timestamp、X-Nonce、X-Signature,禁止复用上一次请求头。
防重放说明:
- 开放平台会按
API Key + X-Nonce记录已通过鉴权的请求,防止请求被重复使用。 - 同一个
API Key下,只要请求仍处于 5 分钟有效时间窗内,重复使用同一个X-Nonce就会被判定为重放请求。 - 无论业务处理最终成功还是失败,只要客户端再次发起 HTTP 请求,都必须视为一次全新的签名请求。
- 如果重复使用同一个
X-Nonce,服务端会直接返回401,错误信息为“请求已被重复使用”。 X-Nonce只用于请求防重放,不是业务幂等键。若三方需要“安全重试但不能重复创建业务数据”,请在业务参数里自行携带独立的幂等标识。
请求体兼容性说明:
- 开放平台网关会尽量兼容三方常见的弱类型 JSON 传参,例如把数字传成数字字符串、把布尔值传成
"true"/"false"或"1"/"0"。 - 可选字段没有值时,推荐直接省略字段,或显式传
null;不要依赖空字符串""表示“空”。当前仅对少量高频场景做了兼容,非法值仍会按参数错误处理。 - 为保证联调稳定,三方仍应优先按字段声明的标准 JSON 类型传值,不要把所有字段统一转成字符串。
JSON 签名与发送必须使用同一份文本:
- POST JSON 请求参与签名的是最终发送的完整 JSON 字符串,不是待序列化的对象。字段顺序、数字格式(例如
1与1.0)、空格、转义和null差异都可能导致签名不一致。 - 不要先用一个序列化器生成 JSON 并签名,再调用客户端库的对象发送方法重新序列化。例如,RestSharp 的
AddJsonBody(body)可能与 Newtonsoft.Json 生成的签名原文不同。 - C# 推荐先生成并保存
jsonBody,签名和请求发送都使用该字符串:JsonConvert.SerializeObject(body)生成后,通过AddParameter("application/json", jsonBody, ParameterType.RequestBody)发送。 - 如果返回 HTTP
401、业务状态40105“签名校验失败”,除检查路径、查询串、请求头和时间戳外,优先记录并逐字比较签名原文中的BODY与实际 HTTP 请求体。
签名示例(GET - 无查询参数)
GET /api/openplatform/labels sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
签名示例(GET - 带中文查询参数)
GET /api/openplatform/employees ?page=1&pageSize=100&keyword=%E5%BC%A0%E6%96%87%E6%B6%9B sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
URL 编码注意事项:
- 如果查询参数包含中文、空格、特殊符号(如
&、=),必须先进行 URL 编码,再参与签名计算。 - C# 使用
Uri.EscapeDataString("张文涛"),编码结果为%E5%BC%A0%E6%96%87%E6%B6%9B。 - JavaScript 使用
encodeURIComponent("张文涛"),编码结果相同。 - 签名原文中的查询串必须与实际发送的 URL 中的查询串完全一致,否则服务端会返回 401 签名校验失败。
- RestSharp 的
AddQueryParameter方法会自动进行 URL 编码,签名时应使用编码后的查询串。
签名示例(POST + JSON Body)
POST
/api/openplatform/tasks
sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1718515200
f4f7ec0b3d574c27a5d1c32f5d4d6e90
emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
{"title":"完成季度复盘","assigneeVirtualIds":["emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz"],"dueAt":"2026-06-18T00:00:00+08:00"}签名示例(POST + multipart/form-data)
POST /api/openplatform/attachments sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz [multipart/form-data]
常见签名错误:
- GET 请求签名时漏掉
?前缀。 - 签名使用的 QueryString 参数顺序与实际 URL 不一致。
- 查询参数包含中文但未进行 URL 编码,导致签名原文与实际发送的 URL 不一致。
- 使用 HTTP 客户端库(如 RestSharp、axios)时,客户端自动编码了查询参数,但签名时使用了未编码的原始值。
- 请求头里传了
X-Employee-Virtual-Id,但签名原文里的EMPLOYEE_VIRTUAL_ID仍然留空,或值不一致。 - 附件上传签名时误把二进制文件正文参与签名,而不是固定占位
[multipart/form-data]。 - POST 请求签名时 JSON 被格式化过,而实际发送时又重新序列化了一次;尤其要检查数字字段是否出现
1与1.0的差异。 - 签名时使用了毫秒级时间戳,或者用了大写十六进制签名串。
- 请求失败后直接复用上一次的
X-Timestamp、X-Nonce、X-Signature重试,导致被判定为重复请求。 - 客户端并发发送多个请求时错误复用了同一个
X-Nonce。