Skip to content

签名算法

算法描述

参与签名的字段分别是:

  • apiPath:URI(必须
  • body:请求体(必须),JSON字符串
  • x-api-key:同appId必须
  • x-api-timestamp:当前时间戳,单位毫秒(必须
  • 其他在URI中的参数

按照以下步骤实现:

  1. 将以上所有参数的名称和值放入contentMap,对contentMapkey进行升序排序
  2. contentMap进行JSON序列化
  3. 使用SHA256算法对JSON字符串+密钥进行加密
  4. 最后对加密结果进行BASE64编码,得到的结果就是签名字符串

如以下示例:

shell

curl -H 'x-api-key: key' \
     -H 'x-api-timestamp: 1744636844000' \
     -H 'x-api-signature: 0xb7Kxxxx=' \
      'https://{HOST}/path/to/pay?param1=test1&param2=test2' \
      -d '{"data":"test"}'

curl -H 'x-api-key: key' \
     -H 'x-api-timestamp: 1744636844000' \
     -H 'x-api-signature: 0xb7Kxxxx=' \
      'https://{HOST}/path/to/pay?param1=test1&param2=test2' \
      -d '{"data":"test"}'

JSON序列化后的contentMap如下:

json
{
  "apiPath": "/path/to/pay",
  "body": {
    "data": "test"
  },
  "param1": "test1",
  "param2": "test2",
  "x-api-key": "key",
  "x-api-timestamp": "17200001"
}
{
  "apiPath": "/path/to/pay",
  "body": {
    "data": "test"
  },
  "param1": "test1",
  "param2": "test2",
  "x-api-key": "key",
  "x-api-timestamp": "17200001"
}

最后对这个JSON字符串进行加密。

代码实现

后端实现示例

go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/url"
    "sort"
)

func GenSign(appId, apiSecret, pathUrl, payload string, tm int64) string {
	// 解析 URL
	path := ""
	contentMap := make(map[string]string)
	if u, err := url.Parse(pathUrl); err == nil {
		path = u.Path
		for k, v := range u.Query() {
			if len(v) > 0 {
				contentMap[k] = v[0]
			}
		}
	}

	// 构建待签名的 map
	contentMap["x-api-key"] = appId
	contentMap["x-api-timestamp"] = fmt.Sprintf("%d", tm) // 毫秒
	contentMap["apiPath"] = path
	contentMap["body"] = payload

	// 获取所有 keys 并排序
	keys := make([]string, 0, len(contentMap))
	for k := range contentMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 创建有序的 map
	orderedMap := make(map[string]string)
	for _, k := range keys {
		orderedMap[k] = contentMap[k]
	}

	// JSON 序列化
	content, _ := json.Marshal(orderedMap)

	// 计算 HMAC
	mac := hmac.New(sha256.New, []byte(apiSecret))
	mac.Write(content)
	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

// 使用示例
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", `{"data":"test"}`, time.Now().UnixMilli())

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/url"
    "sort"
)

func GenSign(appId, apiSecret, pathUrl, payload string, tm int64) string {
	// 解析 URL
	path := ""
	contentMap := make(map[string]string)
	if u, err := url.Parse(pathUrl); err == nil {
		path = u.Path
		for k, v := range u.Query() {
			if len(v) > 0 {
				contentMap[k] = v[0]
			}
		}
	}

	// 构建待签名的 map
	contentMap["x-api-key"] = appId
	contentMap["x-api-timestamp"] = fmt.Sprintf("%d", tm) // 毫秒
	contentMap["apiPath"] = path
	contentMap["body"] = payload

	// 获取所有 keys 并排序
	keys := make([]string, 0, len(contentMap))
	for k := range contentMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 创建有序的 map
	orderedMap := make(map[string]string)
	for _, k := range keys {
		orderedMap[k] = contentMap[k]
	}

	// JSON 序列化
	content, _ := json.Marshal(orderedMap)

	// 计算 HMAC
	mac := hmac.New(sha256.New, []byte(apiSecret))
	mac.Write(content)
	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

// 使用示例
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", `{"data":"test"}`, time.Now().UnixMilli())
php
<?php

function GenSign(
    string $appId,
    string $apiSecret,
    string $pathUrl,
    string $payload,
    int    $tm): string {
    $contentMap = [];
    $parts      = parse_url($pathUrl);

    $path = $parts['path'] ?? '';
    if (!empty($parts['query'])) {
        parse_str($parts['query'], $queryArr);
        foreach ($queryArr as $k => $v) {
            $contentMap[$k] = (string)$v;
        }
    }

    $contentMap['x-api-key']      = $appId;
    $contentMap['x-api-timestamp'] = (string)$tm;
    $contentMap['apiPath']        = $path;
    $contentMap['body']           = $payload;

    ksort($contentMap, SORT_STRING);

    $json = json_encode(
        $contentMap,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    $raw  = hash_hmac('sha256', $json, $apiSecret, true);
    return base64_encode($raw);
}


// 使用示例
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", '{"data":"test"}', floor(microtime(true) * 1000))
<?php

function GenSign(
    string $appId,
    string $apiSecret,
    string $pathUrl,
    string $payload,
    int    $tm): string {
    $contentMap = [];
    $parts      = parse_url($pathUrl);

    $path = $parts['path'] ?? '';
    if (!empty($parts['query'])) {
        parse_str($parts['query'], $queryArr);
        foreach ($queryArr as $k => $v) {
            $contentMap[$k] = (string)$v;
        }
    }

    $contentMap['x-api-key']      = $appId;
    $contentMap['x-api-timestamp'] = (string)$tm;
    $contentMap['apiPath']        = $path;
    $contentMap['body']           = $payload;

    ksort($contentMap, SORT_STRING);

    $json = json_encode(
        $contentMap,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    $raw  = hash_hmac('sha256', $json, $apiSecret, true);
    return base64_encode($raw);
}


// 使用示例
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", '{"data":"test"}', floor(microtime(true) * 1000))

Postman Pre-request Script

用于在Postman中自动生成签名,将以下脚本粘贴到Pre-request Script标签页:

javascript
const appId = 'YOUR_APP_ID';
const apiSecret = 'YOUR_API_SECRET';

const ts = Date.now();

pm.request.headers.upsert({ key: 'x-api-key', value: appId });
pm.request.headers.upsert({ key: 'x-api-timestamp', value: String(ts) });

const path = pm.request.url.getPath();
const rawBody =
  pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';
const bodyForSign = rawBody.replace(/\r?\n/g, '');

const ordered = {
  apiPath: path,
  body: bodyForSign,
  'x-api-key': appId,
  'x-api-timestamp': String(ts),
};

const content = JSON.stringify(ordered);

console.log('Signature content:', ordered);

const signature = CryptoJS.HmacSHA256(content, apiSecret).toString(
  CryptoJS.enc.Base64
);

pm.request.headers.upsert({ key: 'x-api-signature', value: signature });

console.log('Generated signature:', signature);
const appId = 'YOUR_APP_ID';
const apiSecret = 'YOUR_API_SECRET';

const ts = Date.now();

pm.request.headers.upsert({ key: 'x-api-key', value: appId });
pm.request.headers.upsert({ key: 'x-api-timestamp', value: String(ts) });

const path = pm.request.url.getPath();
const rawBody =
  pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';
const bodyForSign = rawBody.replace(/\r?\n/g, '');

const ordered = {
  apiPath: path,
  body: bodyForSign,
  'x-api-key': appId,
  'x-api-timestamp': String(ts),
};

const content = JSON.stringify(ordered);

console.log('Signature content:', ordered);

const signature = CryptoJS.HmacSHA256(content, apiSecret).toString(
  CryptoJS.enc.Base64
);

pm.request.headers.upsert({ key: 'x-api-signature', value: signature });

console.log('Generated signature:', signature);