API 接口请求签名实现
| 版本号 | 日期 | 修订要点 | 备注 |
|---|---|---|---|
| V2.2.1 | 2022/1/7 | 新建 |
一、目的
对接口访问需要进行签名校验,本文提供签名算法说明和参考实现,方便开发人员快速接入。
二、签名算法
签名的生成要素主要由应用 appKey、应用 appSecret、随机数 rand、时间戳(单位秒)timestamp 按照固定的排列规则组成;生成的签名不能重复使用。
签名值生成过程:
- 获取
appKey和appSecret(由服务方提供,下文以占位符代替); - 生成 4~6 位随机字符串;
- 生成当前时间戳(秒);
- 按固定规则拼接得到 plain 字符串:
- 排列方式:
appKey=%s&appSecret=%s&rand=%s×tamp=%s
- 排列方式:
- 使用
HmacSHA256以appSecret为密钥对 plain 字符串做消息摘要; - HTTP 请求头携带签名相关信息:
x-appKey(应用 appKey)、x-signature(签名)、x-timestamp(时间戳)、x-rand(随机数)。
三、算法参考
Java
随机数生成:
1public static final String BASE = "abcdefghijklmnopqrstuvwxyz0123456789";
2
3/**
4 * 随机生成 4-6 位字符串
5 */
6public static String randStr() {
7 StringBuilder sb = new StringBuilder();
8 int length = (int) (Math.random() * 3 + 4);
9 for (int i = 0; i < length; i++) {
10 int number = (int) (Math.random() * BASE.length());
11 sb.append(BASE.charAt(number));
12 }
13 return sb.toString();
14}
时间戳(秒):
1/**
2 * 获取当前时间戳
3 *
4 * @return 时间戳字符串
5 */
6public static String timestampStr() {
7 long second = Instant.now().getEpochSecond();
8 return String.valueOf(second);
9}
签名:
1public static String getSign(String appKey, String appSecret, String rand, String timestamp) throws Exception {
2 // 有随机数的签名
3 String raw = "appKey=%s&appSecret=%s&rand=%s×tamp=%s";
4 String plain = String.format(raw, appKey, appSecret, rand, timestamp);
5
6 SecretKeySpec secretKeySpec = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
7 Mac mac = Mac.getInstance("HmacSHA256");
8 mac.init(secretKeySpec);
9 byte[] bytes = mac.doFinal(plain.getBytes());
10 return byte2HexString(bytes);
11}
12
13public static String byte2HexString(byte[] bytes) {
14 StringBuilder stringBuffer = new StringBuilder();
15 for (int i = 0; i < bytes.length; ++i) {
16 String temp = Integer.toHexString(bytes[i] & 255);
17 if (temp.length() == 1) {
18 stringBuffer.append("0");
19 }
20 stringBuffer.append(temp);
21 }
22 return stringBuffer.toString();
23}
Golang
随机数:
1import (
2 "bytes"
3 "crypto/rand"
4 "math/big"
5 mrand "math/rand"
6)
7
8// BASE 随机字符串
9const BASE = "abcdefghijklmnopqrstuvwxyz0123456789"
10
11func RandStr() string {
12 var randStr string
13 b := bytes.NewBufferString(BASE)
14 length := 4 + mrand.Intn(3)
15 bigInt := big.NewInt(int64(b.Len()))
16 for i := 0; i < length; i++ {
17 randomInt, _ := rand.Int(rand.Reader, bigInt)
18 randStr += string(BASE[randomInt.Int64()])
19 }
20 return randStr
21}
签名:
1package main
2
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "fmt"
8)
9
10func CreateAccessSign(appKey, appSecret, rand, timestamp string) string {
11 // 有随机数的签名
12 plain := fmt.Sprintf("appKey=%s&appSecret=%s&rand=%s×tamp=%s", appKey, appSecret, rand, timestamp)
13 key := []byte(appSecret)
14 h := hmac.New(sha256.New, key)
15 h.Write([]byte(plain))
16 return hex.EncodeToString(h.Sum(nil))
17}
Python
1import hmac, time, random
2from hashlib import sha256
3
4
5def hmac_sha256(key, plain):
6 sign = hmac.new(key, plain, sha256).hexdigest()
7 return sign
8
9
10if __name__ == '__main__':
11 appKey = '[接入方 appKey]' # 应用 appKey
12 appSecret = '[接入方 appSecret]' # 应用密钥
13 rand = random.randint(100000, 900000) # 6位随机数
14 timestamp = int(time.time())
15 raw = "appKey={}&appSecret={}&rand={}×tamp={}".format(appKey, appSecret, rand, timestamp)
16 sign = hmac_sha256(appSecret.encode(), raw.encode())
17 print(sign)
JS
1var appKey = '[接入方 appKey]';
2var appSecret = '[接入方 appSecret]';
3var timestamp = Math.round(new Date().getTime() / 1000); // 获取秒数时间戳
4var rand = Math.round(100000 + Math.random() * 900000); // 6位随机数
5var magic = `appKey=${appKey}&appSecret=${appSecret}&rand=${rand}×tamp=${timestamp}`;
6var signature = CryptoJS.HmacSHA256(magic, appSecret).toString();
四、请求示例
以 Java 为例,提供一个携带签名的接口访问示例:
1@Test
2public void signTest() throws Exception {
3 // 应用 appKey
4 String appKey = "[接入方 appKey]";
5 // 应用密钥
6 String appSecret = "[接入方 appSecret]";
7 // 随机数
8 String rand = randStr();
9 // 时间戳
10 String timestamp = timestampStr();
11 // 生成签名
12 String sign = getSign(appKey, appSecret, rand, timestamp);
13 // 使用 http 访问接口
14 // 请求头携带 x-appKey、x-signature、x-timestamp、x-rand 参数
15 OkHttpClient client = new OkHttpClient();
16 String json = "";
17 String url = "http://your-server/api/v1/xxx";
18 RequestBody body = RequestBody.create(json, MediaType.get("application/json; charset=utf-8"));
19 Request request = new Request.Builder()
20 .addHeader("x-appKey", appKey)
21 .addHeader("x-signature", sign)
22 .addHeader("x-timestamp", timestamp)
23 .addHeader("x-rand", rand)
24 .url(url)
25 .post(body)
26 .build();
27 try (Response response = client.newCall(request).execute()) {
28 String bodyStr = response.body().string();
29 // 打印响应结果
30 System.out.println(bodyStr);
31 }
32}
注意各语言的差异点:示例中的 rand 有的是 4~6 位字母数字串、有的是 6 位纯数字,接入时以服务方约定的规则为准,签名前 plain 串的拼装顺序必须与服务端完全一致。
