Files
mp-java/docs/一站式平台推送-接口鉴权规范.md

6.2 KiB
Raw Permalink Blame History

汇吉采 → 一站式代理平台:标书订单推送接口鉴权规范

用途:本文档是本平台(汇吉采标书购买平台,下称「官方网」)调用一站式代理平台接口时的鉴权规范,用于对接一站式平台开发人员,请对方按此规则在服务端做校验。 目标接口: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 签名时间,格式 yyyyMMddHHmm24 小时制,北京时区,精确到分钟)
sign 9f7c…(32 位小写十六进制) 签名值,算法见第 3 节

另需:Content-Type: application/json


3. 签名算法

sign = MD5( appKey + password + timestamp )
  • + 表示字符串直接拼接,中间无分隔符。
  • timestamp 使用与请求头 timestamp 完全相同的值。
  • MD5 输出小写 32 位十六进制字符串。

步骤:

  1. 取当前北京时间的分钟表示 timestamp = yyyyMMddHHmm(例:2026-04-08 01:03202604080103)。
  2. 拼串:HJC_Official_Website + vQ8$kR3#mW6@xP2!nF + 202604080103
  3. 对该字符串做 MD5,转小写,得到 sign
  4. appKeytimestampsign 放入请求头发出。

4. 校验规则(一站式平台服务端)

收请求后按以下顺序校验,任一步不通过即拒绝(建议返回 401 / 400,并附错误码与说明,便于排查):

  1. 校验 appKeyappKey 是否等于已登记的应用标识 HJC_Official_Website。不等 → 拒绝。
  2. 校验 timestamp 时效(防重放)timestamp一站式服务器当前时间之差是否在 ±10 分钟以内(统一按北京时区、分钟精度)。超出窗口 → 拒绝,提示「时间戳失效」。
    • 该窗口同时限制了重放窗口;如需更严可协商收紧到 ±3 分钟。
  3. 校验签名:用共享的 password 重算 MD5(appKey + password + timestamp),与请求头 sign 不区分大小写比较(统一小写后比较)。不一致 → 拒绝,提示「签名校验失败」。
  4. 校验通过后,按请求体 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、passwordappKey+年月日时分、password+年月日时分,MD5 加密」。若对方已按此固定算法实现,则采用:

  • 请求头:appKey=HJC_Official_Websitepassword=vQ8$kR3#mW6@xP2!nFsign=MD5(password + yyyyMMddHHmm)(小写)。
  • 校验:password 需与约定值一致,且 MD5(password + timestamp) == sign,同时 timestamp 在 ±10 分钟内。

该方案会把 password 以明文形式暴露在报文中,安全性差;仅作兼容退路,不作为默认推荐。若对方无法确认固定算法,请首选第 1–6 节的方案。


附:需要一站式平台确认/提供的信息

  1. 上述 sign 拼接顺序是否与合作方既有算法一致(appKey+password+timestamp)。
  2. 时间戳格式 yyyyMMddHHmm 与北京时区是否认可。
  3. 校验窗口 ±10 分钟是否可接受。
  4. createPurchaseDetails 请求体字段表(本平台另一份文档,见需求/接口清单)的具体入参名与必填项,需对方给出或双方对齐,以免字段名不一致导致落盘失败。