# 公共签名

## 系统级 Header

* 【必选】X-Sy-Key：AppKey &#x20;
* 【必选】X-Sy-Signature：签名字符串 &#x20;
* 【必选】X-Sy-Timestamp：API调用者传递时间戳，值为当前时间的秒数，也就是从1970年1月1日起至今的时间转换为秒，时间戳有效时间为15分钟。 &#x20;
* 【必选】X-Sy-Nonce：API调用者生成的 UUID，结合时间戳防重放。

## 请求签名

对于每一次HTTP或者HTTPS协议请求，我们会根据访问中的签名信息验证访问请求者身份。具体由使用appKey和appSecret对称加密验证实现。其中appKey是访问者身份，appSecret是加密签名字符串和服务器端验证签名字符串的密钥，必须严格保密谨防泄露。

### 1. 指定请求参数

在代码中指定请求参数，参数中需要包含系统级Header和接口必备的参数信息。

**说明：** 请求参数中不允许出现以signature为key的参数。

示例代码如下：

```
String appKey = "testKsy";
String appSecret = "testSecret";
String timestamp = String.valueOf(System.currentTimeMillis()/1000);
String signNonce = java.util.UUID.randomUUID().toString().replaceAll("-", "");
java.util.Map<String, String> paras = new java.util.HashMap<String, String>();
```

指定参数：

```
paras.put("appKey", testKey);
paras.put("timestamp", timestamp);
paras.put("signNonce", signNonce);

paras.put("name", "okok");
paras.put("mobile", "0999999999");
paras.put("credential_no", "1111581111");
```

去除签名关键字Key：

```
if (paras.containsKey("signature"))
paras.remove("signature");
```

### 2. 根据参数Key排序（升序）

参考代码如下：

```
java.util.TreeMap<String, String> sortParas = new java.util.TreeMap<String, String>(paras);
```

### 3. 构造待签名的请求串

在一般的URLEncode后再增加三种字符替换：加号`（+）`替换成 `%20`、星号`（*）`替换成 `%2A`、`%7E` 替换回波浪号`（~）`参考代码如下：

```
public static String specialUrlEncode(String value) throws Exception {
    return java.net.URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
```

构造待签名的请求串：

1. 把排序后的参数顺序拼接成如下格式：

   ```
   * specialUrlEncode(参数Key) + "=" + specialUrlEncode(参数值)
   ```

   参考代码如下：

   ```
   java.util.Iterator<String> it = sortParas.keySet().iterator();
   StringBuilder sortQueryString = new StringBuilder();
   while (it.hasNext()) {
       String key = it.next();
       sortQueryString.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
   }
   String sortedQueryString = sortQueryString.substring(1); //去除第一个多余的&符号
   ```

### 4. 签名

签名采用HmacSHA1算法 + Base64，编码采用UTF-8。参考代码如下：

```
String sign = sign(appSecret, sortedQueryString);

public static String sign(String appSecret, String stringToSign) throws Exception {
    javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
    mac.init(new javax.crypto.spec.SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA1"));
    byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
    return new sun.misc.BASE64Encoder().encode(signData);
}
```

参数说明：

1. `appSecret`：你的`appKey`对应的秘钥`appSecret` &#x20;
2. `stringToSign`：即第三步生成的待签名请求串

### 5. 增加签名结果到请求参数中，发送请求

**说明：** 签名也要做特殊URL编码。

```
String signature = specialUrlEncode(sign);
```
