签名示例
本文档提供了不同编程语言的签名实现示例。所有示例都实现了相同的签名逻辑:
- 移除签名字段
- 过滤空值并按键名排序
- 拼接参数
- 使用 HMAC-SHA256 算法生成签名
Python 示例
import hmac
import hashlib
from collections import OrderedDict
from urllib.parse import quote_plus
class SignatureGenerator:
def build_sign_content(self, data):
# 1. 移除签名字段
sign_data = data.copy()
sign_data.pop('sign', None)
# 2. 过滤空值并按键名排序
filtered_items = [
(k, self._convert_to_string(v))
for k, v in sign_data.items()
if self._is_valid_value(v)
]
ordered_items = OrderedDict(sorted(filtered_items))
# 3. 拼接参数
return '&'.join(
f"{k}={v}" for k, v in ordered_items.items() if v != ''
)
def _is_valid_value(self, value):
if value is None:
return False
if isinstance(value, (dict, list)) and not value:
return False
return True
def _convert_to_string(self, value):
if value is None:
return ''
# 处理字典类型
if isinstance(value, dict):
if not value:
return ''
return self.build_sign_content(value)
# 处理列表类型
if isinstance(value, list):
if not value:
return ''
return ','.join(sorted(
self._convert_to_string(item) for item in value if item is not None
))
# 处理基本类型,进行 URL 编码
try:
return quote_plus(str(value))
except Exception:
return str(value)
def generate_sign(self, data, secret_key):
# 1. 构建签名内容
content = self.build_sign_content(data)
# 2. 使用 HMAC-SHA256 生成签名
hmac_obj = hmac.new(
secret_key.encode('utf-8'),
content.encode('utf-8'),
hashlib.sha256
)
return hmac_obj.hexdigest().upper()
# 使用示例
request_data = {
"amount": 1000000,
"channelCode": "DAN_530727",
"currency": "IDR",
"merchantOrderNo": "25050817311353902557",
"notificationUrl": "https://api.example.com/notify",
"channelParams": {
"bankCode": "ICBC",
"cardInfo": {
"cardNo": "6222021234567890",
"holders": ["张三", "李四"]
}
}
}
secret_key = 'your_secret_key'
generator = SignatureGenerator()
sign = generator.generate_sign(request_data, secret_key)
print(f"签名内容: {generator.build_sign_content(request_data)}")
print(f"生成的签名: {sign}")
JavaScript 示例
class SignatureGenerator {
buildSignContent(data) {
// 1. 移除签名字段
const signData = { ...data };
delete signData.sign;
// 2. 过滤空值并按键名排序
const filteredItems = Object.entries(signData)
.filter(([_, value]) => this.isValidValue(value))
.map(([key, value]) => [key, this.convertToString(value)])
.sort(([a], [b]) => a.localeCompare(b));
// 3. 拼接参数
return filteredItems
.filter(([_, value]) => value !== '')
.map(([key, value]) => `${key}=${value}`)
.join('&');
}
isValidValue(value) {
if (value === null || value === undefined) {
return false;
}
if (typeof value === 'object' && Object.keys(value).length === 0) {
return false;
}
return true;
}
convertToString(value) {
if (value === null || value === undefined) {
return '';
}
// 处理对象类型
if (typeof value === 'object' && !Array.isArray(value)) {
if (Object.keys(value).length === 0) {
return '';
}
return this.buildSignContent(value);
}
// 处理数组类型
if (Array.isArray(value)) {
if (value.length === 0) {
return '';
}
return value
.filter(item => item != null)
.map(item => this.convertToString(item))
.sort()
.join(',');
}
// 处理基本类型,进行 URL 编码
try {
return encodeURIComponent(String(value));
} catch {
return String(value);
}
}
async generateSign(data, secretKey) {
// 1. 构建签名内容
const content = this.buildSignContent(data);
// 2. 使用 HMAC-SHA256 生成签名
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secretKey),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
encoder.encode(content)
);
return Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
.toUpperCase();
}
}
// 使用示例
const requestData = {
amount: 1000000,
channelCode: 'DAN_530727',
currency: 'IDR',
merchantOrderNo: '25050817311353902557',
notificationUrl: 'https://api.example.com/notify',
channelParams: {
bankCode: 'ICBC',
cardInfo: {
cardNo: '6222021234567890',
holders: ['张三', '李四']
}
}
};
const secretKey = 'your_secret_key';
const generator = new SignatureGenerator();
generator.generateSign(requestData, secretKey).then(sign => {
console.log('签名内容:', generator.buildSignContent(requestData));
console.log('生成的签名:', sign);
});
Java 示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
public class SignatureGenerator {
public String buildSignContent(Map<String, Object> data) {
// 1. 移除签名字段
Map<String, Object> signData = new HashMap<>(data);
signData.remove("sign");
// 2. 过滤空值并按键名排序
return signData.entrySet().stream()
.filter(entry -> isValidValue(entry.getValue()))
.map(entry -> new AbstractMap.SimpleEntry<>(
entry.getKey(),
convertToString(entry.getValue())
))
.filter(entry -> !entry.getValue().isEmpty())
.sorted(Map.Entry.comparingByKey())
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
}
private boolean isValidValue(Object value) {
if (value == null) {
return false;
}
if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
return false;
}
if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
return false;
}
return true;
}
private String convertToString(Object value) {
if (value == null) {
return "";
}
// 处理 Map 类型
if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
if (map.isEmpty()) {
return "";
}
return buildSignContent((Map<String, Object>) value);
}
// 处理 Collection 类型
if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
if (collection.isEmpty()) {
return "";
}
return collection.stream()
.filter(Objects::nonNull)
.map(this::convertToString)
.sorted()
.collect(Collectors.joining(","));
}
// 处理基本类型,进行 URL 编码
try {
return URLEncoder.encode(String.valueOf(value), StandardCharsets.UTF_8.name());
} catch (Exception e) {
return String.valueOf(value);
}
}
public String generateSign(Map<String, Object> data, String secretKey) throws Exception {
// 1. 构建签名内容
String content = buildSignContent(data);
// 2. 使用 HMAC-SHA256 生成签名
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8),
"HmacSHA256"
);
hmacSha256.init(secretKeySpec);
byte[] hash = hmacSha256.doFinal(content.getBytes(StandardCharsets.UTF_8));
// 转换为十六进制字符串并转大写
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString().toUpperCase();
}
}
// 使用示例
public class Main {
public static void main(String[] args) throws Exception {
Map<String, Object> requestData = new HashMap<>();
requestData.put("amount", 1000000);
requestData.put("channelCode", "DAN_530727");
requestData.put("currency", "IDR");
requestData.put("merchantOrderNo", "25050817311353902557");
requestData.put("notificationUrl", "https://api.example.com/notify");
Map<String, Object> channelParams = new HashMap<>();
channelParams.put("bankCode", "ICBC");
Map<String, Object> cardInfo = new HashMap<>();
cardInfo.put("cardNo", "6222021234567890");
cardInfo.put("holders", Arrays.asList("张三", "李四"));
channelParams.put("cardInfo", cardInfo);
requestData.put("channelParams", channelParams);
String secretKey = "your_secret_key";
SignatureGenerator generator = new SignatureGenerator();
String sign = generator.generateSign(requestData, secretKey);
System.out.println("签名内容: " + generator.buildSignContent(requestData));
System.out.println("生成的签名: " + sign);
}
}
PHP 示例
<?php
class SignatureGenerator {
public function buildSignContent($data) {
// 1. 移除签名字段
$signData = $data;
unset($signData['sign']);
// 2. 过滤空值并按键名排序
$filteredItems = array_filter(
$signData,
[$this, 'isValidValue']
);
// 3. 转换值并按键名排序
$items = [];
foreach ($filteredItems as $key => $value) {
$convertedValue = $this->convertToString($value);
if ($convertedValue !== '') {
$items[$key] = $convertedValue;
}
}
ksort($items);
// 4. 拼接参数
return implode('&', array_map(
function($key, $value) {
return $key . '=' . $value;
},
array_keys($items),
array_values($items)
));
}
private function isValidValue($value) {
if ($value === null) {
return false;
}
if (is_array($value) && empty($value)) {
return false;
}
return true;
}
private function convertToString($value) {
if ($value === null) {
return '';
}
// 处理数组类型
if (is_array($value)) {
if (empty($value)) {
return '';
}
if ($this->isAssociativeArray($value)) {
return $this->buildSignContent($value);
} else {
$values = array_filter($value, function($item) {
return $item !== null;
});
$values = array_map([$this, 'convertToString'], $values);
sort($values);
return implode(',', $values);
}
}
// 处理基本类型,进行 URL 编码
try {
return urlencode((string)$value);
} catch (Exception $e) {
return (string)$value;
}
}
private function isAssociativeArray($array) {
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
public function generateSign($data, $secretKey) {
// 1. 构建签名内容
$content = $this->buildSignContent($data);
// 2. 使用 HMAC-SHA256 生成签名
$signature = hash_hmac('sha256', $content, $secretKey);
return strtoupper($signature);
}
}
// 使用示例
$requestData = [
'amount' => 1000000,
'channelCode' => 'DAN_530727',
'currency' => 'IDR',
'merchantOrderNo' => '25050817311353902557',
'notificationUrl' => 'https://api.example.com/notify',
'channelParams' => [
'bankCode' => 'ICBC',
'cardInfo' => [
'cardNo' => '6222021234567890',
'holders' => ['张三', '李四']
]
]
];
$secretKey = 'your_secret_key';
$generator = new SignatureGenerator();
$sign = $generator->generateSign($requestData, $secretKey);
echo "签名内容: " . $generator->buildSignContent($requestData) . "\n";
echo "生成的签名: " . $sign . "\n";
Go 示例
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"sort"
"strings"
)
type SignatureGenerator struct{}
func (g *SignatureGenerator) buildSignContent(data map[string]interface{}) string {
// 1. 移除签名字段
signData := make(map[string]interface{})
for k, v := range data {
if k != "sign" {
signData[k] = v
}
}
// 2. 过滤空值并按键名排序
keys := make([]string, 0, len(signData))
for k, v := range signData {
if g.isValidValue(v) {
keys = append(keys, k)
}
}
sort.Strings(keys)
// 3. 拼接参数
pairs := make([]string, 0, len(keys))
for _, k := range keys {
v := g.convertToString(signData[k])
if v != "" {
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
}
}
return strings.Join(pairs, "&")
}
func (g *SignatureGenerator) isValidValue(value interface{}) bool {
if value == nil {
return false
}
switch v := value.(type) {
case map[string]interface{}:
return len(v) > 0
case []interface{}:
return len(v) > 0
default:
return true
}
}
func (g *SignatureGenerator) convertToString(value interface{}) string {
if value == nil {
return ""
}
switch v := value.(type) {
case map[string]interface{}:
if len(v) == 0 {
return ""
}
return g.buildSignContent(v)
case []interface{}:
if len(v) == 0 {
return ""
}
values := make([]string, 0, len(v))
for _, item := range v {
if item != nil {
values = append(values, g.convertToString(item))
}
}
sort.Strings(values)
return strings.Join(values, ",")
default:
return url.QueryEscape(fmt.Sprintf("%v", v))
}
}
func (g *SignatureGenerator) generateSign(data map[string]interface{}, secretKey string) string {
// 1. 构建签名内容
content := g.buildSignContent(data)
// 2. 使用 HMAC-SHA256 生成签名
h := hmac.New(sha256.New, []byte(secretKey))
h.Write([]byte(content))
return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
}
func main() {
requestData := map[string]interface{}{
"amount": 1000000,
"channelCode": "DAN_530727",
"currency": "IDR",
"merchantOrderNo": "25050817311353902557",
"notificationUrl": "https://api.example.com/notify",
"channelParams": map[string]interface{}{
"bankCode": "ICBC",
"cardInfo": map[string]interface{}{
"cardNo": "6222021234567890",
"holders": []interface{}{"张三", "李四"},
},
},
}
secretKey := "your_secret_key"
generator := &SignatureGenerator{}
sign := generator.generateSign(requestData, secretKey)
fmt.Printf("签名内容: %s\n", generator.buildSignContent(requestData))
fmt.Printf("生成的签名: %s\n", sign)
}
注意事项
- 所有示例都实现了相同的签名逻辑,但具体实现细节可能因语言特性而略有不同
- 示例中的
your_secret_key
需要替换为实际的密钥 - 建议在生产环境中添加适当的错误处理和日志记录
- 请确保使用安全的随机数生成器来生成密钥
- 定期更换密钥以提高安全性