×

🔐 京东JOS API Sign签名算法详解与MD5实现(Java / Python / PHP 三版)

万邦科技Lex 万邦科技Lex 发表于2026-07-05 14:35:40 浏览20 评论0

抢沙发发表评论

🔐 京东JOS API Sign签名算法详解与MD5实现(Java / Python / PHP 三版)

京东开放平台(JOS)统一使用 MD5签名,规则和1688/TOP非常接近但有两个关键差异:
  • 时间戳用秒级(10位),不是毫秒

  • 业务参数必须放 360buy_param_json(紧凑JSON字符串)参与签名


一、JOS 签名算法官方步骤(背下来)

假设 App Key = 123456App Secret = abcdefg
  1. 收集所有API参数(含公共参数):

    • app_key, method, timestamp, format, v, sign_method

    • 360buy_param_json(值为业务参数字典的紧凑JSON字符串

    • 若有 access_token(订单类)也参入

  2. 剔除sign字段、值为 None或空字符串、file二进制

  3. 按参数名 ASCII 升序排序

  4. 拼接 key+value(无 =&
    app_key123456formatjsonmethodjingdong.ware.read.gettimestamp1700000000v2.0360buy_param_json{"wareId":"1000123","fields":"title,price"}

  5. 首尾拼 AppSecret
    AppSecret + 上步字符串 + AppSecret

  6. MD5 → 32位大写

sign = MD5( AppSecret + KV_ASCII_sorted + AppSecret ).upper()
⚠️ 360buy_param_json值必须是 JSON紧凑格式(无空格,中文 ensure_ascii=False),原样参与签名,不 URL-Encode 再签名。

二、Python 实现(推荐直接用)

# jd_jos_sign.py
import hashlib
import json
from typing import Dict, Optional
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex

def jd_sign(params: Dict[str, Optional[str]], app_secret: str) -> str:
    """
    JOS MD5 Sign
    params: 所有API参数(含 app_key/method/timestamp/360buy_param_json/access_token)
            不包含 sign
    空值/None 剔除
    """
    filt = {
        k: v for k, v in params.items()
        if v is not None and str(v).strip() != "" and k != "sign"
    }

    # ASCII 升序
    sorted_items = sorted(filt.items(), key=lambda x: x[0])

    # 拼 key+value
    qs = "".join(f"{k}{v}" for k, v in sorted_items)

    # 首尾拼 secret → MD5 → 大写
    raw = f"{app_secret}{qs}{app_secret}"
    return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()


# ===================== 验证示例 =====================
if __name__ == "__main__":
    APP_SECRET = "YOUR_JD_APP_SECRET"

    # 业务参数 → 紧凑JSON
    biz = {"wareId": "100012345678",
           "fields": "ware_id,title,price,jd_price,stock_num,skus_json"}

    api_params = {
        "app_key": "YOUR_JD_APP_KEY",
        "method": "jingdong.ware.read.get",
        "timestamp": str(int(__import__('time').time())),   # ← 秒级!
        "format": "json",
        "v": "2.0",
        "sign_method": "md5",
        "360buy_param_json": json.dumps(biz, ensure_ascii=False,
                                         separators=(',', ':'))   # 紧凑
        # "access_token": "SELLER_TOKEN"   # 订单类接口加这句
    }

    sign = jd_sign(api_params, APP_SECRET)
    api_params["sign"] = sign

    print("✅ sign =", sign)
    print("\n发送参数示例:")
    for k, v in api_params.items():
        print(f"  {k} = {v if k!='sign' else sign}")

三、Java 实现

// JdSignUtil.java
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
public class JdSignUtil {

    public static String sign(Map<String, String> params, String appSecret)
            throws Exception {

        // 1. 剔除 sign + 空值
        TreeMap<String, String> tree = new TreeMap<>();
        for (Map.Entry<String, String> e : params.entrySet()) {
            if (!"sign".equals(e.getKey())
                && e.getValue() != null
                && !e.getValue().trim().isEmpty()) {
                tree.put(e.getKey(), e.getValue());
            }
        }

        // 2. 拼 key+value
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> e : tree.entrySet()) {
            sb.append(e.getKey()).append(e.getValue());
        }

        // 3. 首尾拼 secret
        String toSign = appSecret + sb.toString() + appSecret;
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(toSign.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02X", b & 0xFF));
        }
        return hex.toString();   // 已大写
    }

    // 用法:
    // Map<String,String> p = new HashMap<>();
    // p.put("method","jingdong.ware.read.get");
    // p.put("app_key","KEY");
    // p.put("timestamp", String.valueOf(System.currentTimeMillis()/1000)); // 秒!
    // p.put("format","json"); p.put("v","2.0"); p.put("sign_method","md5");
    // p.put("360buy_param_json", "{\"wareId\":\"1000123\",\"fields\":\"title,price\"}");
    // String sign = JdSignUtil.sign(p, APP_SECRET);
}

四、PHP 实现

<?php
// jd_sign.php
function jdSign(array $params, string $appSecret): string {
    unset($params['sign']);
    $filtered = array_filter($params, fn($v) => $v !== null && $v !== '');
      # 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
    ksort($filtered);
    $str = '';
    foreach ($filtered as $k => $v) {
        $str .= $k . $v;
    }
    $toSign = $appSecret . $str . $appSecret;
    return strtoupper(md5($toSign));
}

// ---- 示例 ----
$params = [
    'method'           => 'jingdong.ware.read.get',
    'app_key'          => 'YOUR_JD_APP_KEY',
    'timestamp'        => (string)(time()),          // ← 秒级
    'format'           => 'json',
    'v'                => '2.0',
    'sign_method'      => 'md5',
    '360buy_param_json'=> json_encode(
        ['wareId'=>'100012345678','fields'=>'ware_id,title,price'],
        JSON_UNESCAPED_UNICODE
    )
];
$sign = jdSign($params, 'YOUR_JD_APP_SECRET');
$params['sign'] = $sign;
echo "sign = {$sign}\n";
?>

五、签名排错清单(JOS特有)

现象
原因
排查
Invalid Signature
timestamp 用毫秒
必须 int(time.time())秒级
Invalid Signature
360buy_param_json含空格/\n
separators=(',',':')/ JSON_UNESCAPED_UNICODE紧凑
Invalid Signature
空值参入签名
严格过滤 None/''
sign fail偶发
系统时钟偏差大
服务器 NTP 同步(±5分钟内)
商品接口OK订单403
非签名问题
订单需企业应用+卖家AccessToken,见之前订单对接文
360buy_param_jsonURL编码再签
签错
先签原始JSON串,签完再整体URL Encode放POST body

六、面试/对接一句话

京东JOS签名 = 参数按key ASCII升序拼key+value(含360buy_param_json紧凑JSON串,空值/sign剔除)→ 首尾拼AppSecret→ MD5 → 大写;timestamp必须用秒级10位,不同于1688的毫秒。
需要我补 京东商品全量SKU翻页同步京东订单增量同步APScheduler(断点续跑+Token刷新) 完整脚本吗?


群贤毕至

访客