6.2 KiB
6.2 KiB
汇吉采 → 一站式代理平台:标书订单推送接口鉴权规范
用途:本文档是本平台(汇吉采标书购买平台,下称「官方网」)调用一站式代理平台接口时的鉴权规范,用于对接一站式平台开发人员,请对方按此规则在服务端做校验。 目标接口:
POST /api/biz/createPurchaseDetails(本平台推送购买记录与订单信息到一站式平台落盘)
1. 共享凭证(双方提前约定,不通过网络传输)
| 参数 | 值 | 说明 |
|---|---|---|
appKey |
HJC_Official_Website |
调用方标识,明文放在请求头 |
password |
vQ8$kR3#mW6@xP2!nF |
签名密钥(Secret),仅双方服务端持有,不放在请求头、不出现于任何请求/日志/报文 |
⚠️ 出于安全考虑,本规范不把
password明文放请求头。原始需求中「把 password 明文字段加到请求头」的做法会泄露密钥,改为「用 password 参与签名、请求头只带签名结果」的等价且更安全的方案(见第 3 节)。如需严格沿用原始「明文 password 头」方案,见附录 A。
2. 请求头(请求方——官方网——每次调用携带 3 个)
| Header | 示例值 | 说明 |
|---|---|---|
appKey |
HJC_Official_Website |
明文 appKey(固定值) |
timestamp |
202604080103 |
签名时间,格式 yyyyMMddHHmm(24 小时制,北京时区,精确到分钟) |
sign |
9f7c…(32 位小写十六进制) |
签名值,算法见第 3 节 |
另需:Content-Type: application/json。
3. 签名算法
sign = MD5( appKey + password + timestamp )
+表示字符串直接拼接,中间无分隔符。timestamp使用与请求头timestamp完全相同的值。MD5输出小写 32 位十六进制字符串。
步骤:
- 取当前北京时间的分钟表示
timestamp = yyyyMMddHHmm(例:2026-04-08 01:03→202604080103)。 - 拼串:
HJC_Official_Website+vQ8$kR3#mW6@xP2!nF+202604080103。 - 对该字符串做 MD5,转小写,得到
sign。 - 将
appKey、timestamp、sign放入请求头发出。
4. 校验规则(一站式平台服务端)
收请求后按以下顺序校验,任一步不通过即拒绝(建议返回 401 / 400,并附错误码与说明,便于排查):
- 校验 appKey:
appKey是否等于已登记的应用标识HJC_Official_Website。不等 → 拒绝。 - 校验 timestamp 时效(防重放):
timestamp与一站式服务器当前时间之差是否在 ±10 分钟以内(统一按北京时区、分钟精度)。超出窗口 → 拒绝,提示「时间戳失效」。- 该窗口同时限制了重放窗口;如需更严可协商收紧到 ±3 分钟。
- 校验签名:用共享的
password重算MD5(appKey + password + timestamp),与请求头sign不区分大小写比较(统一小写后比较)。不一致 → 拒绝,提示「签名校验失败」。 - 校验通过后,按请求体
createPurchaseDetails的字段约定解析并落盘(业务字段见单独一节的「createPurchaseDetails 请求体字段表」)。
5. 幂等与重试约定(可选但强烈建议)
createPurchaseDetails 是「落盘」类写操作,本平台侧会做失败重试,因此建议一站式平台按业务主键幂等处理,避免重复落盘:
- 请求体需带唯一业务单号(如
purchaseId/orderNo),一站式平台以此作为去重键;同一单号重复提交时应返回成功、不重复插入(或返回「已存在」)。 - 本平台对失败的推送会按
attempt递增重试(建议:1/2/4/8 分钟间隔),重试时timestamp/sign需要重新计算(每次请求都要以新时间重签)。
6. 双端示例
官方网(请求方)——以 Python 为例:
import hashlib, time, requests
app_key = "HJC_Official_Website"
password = "vQ8$kR3#mW6@xP2!nF"
ts = time.strftime("%Y%m%d%H%M") # 202604080103
sign = hashlib.md5((app_key + password + ts).encode("utf-8")).hexdigest()
resp = requests.post(
"https://<一站式域名>/api/biz/createPurchaseDetails",
json={ ... 业务字段 ... },
headers={
"appKey": app_key,
"timestamp": ts,
"sign": sign,
"Content-Type": "application/json",
},
timeout=15,
)
一站式(校验方)——以 Java 为例:
String appKey = req.getHeader("appKey");
String ts = req.getHeader("timestamp");
String sign = req.getHeader("sign");
if (!"HJC_Official_Website".equals(appKey)) return 401; // 1
if (Math.abs(System.currentTimeMillis() / 60000 - parseMinute(ts)) > 10)
return 401; // 2
String expect = md5("HJC_Official_Website" + SECRET + ts); // 3
if (!expect.equalsIgnoreCase(sign)) return 401;
// 4. 解析 body,按 createPurchaseDetails 字段落盘(按 orderNo 幂等)
附录 A:若一站式平台坚持「明文 password 头」的原始方案
原始需求表述为「请求头添加 appKey、password,appKey+年月日时分、password+年月日时分,MD5 加密」。若对方已按此固定算法实现,则采用:
- 请求头:
appKey=HJC_Official_Website、password=vQ8$kR3#mW6@xP2!nF、sign=MD5(password + yyyyMMddHHmm)(小写)。 - 校验:
password需与约定值一致,且MD5(password + timestamp) == sign,同时timestamp在 ±10 分钟内。
该方案会把
password以明文形式暴露在报文中,安全性差;仅作兼容退路,不作为默认推荐。若对方无法确认固定算法,请首选第 1–6 节的方案。
附:需要一站式平台确认/提供的信息
- 上述
sign拼接顺序是否与合作方既有算法一致(appKey+password+timestamp)。 - 时间戳格式
yyyyMMddHHmm与北京时区是否认可。 - 校验窗口 ±10 分钟是否可接受。
createPurchaseDetails请求体字段表(本平台另一份文档,见需求/接口清单)的具体入参名与必填项,需对方给出或双方对齐,以免字段名不一致导致落盘失败。