# 签名

## 1. 签名说明

### 1.1. 更新历史

| 版本号   | 日期         | 撰写人 | 说明         |
| ----- | ---------- | --- | ---------- |
| 1.0.0 | 2024-04-10 | -   | 基本信息以及签名方式 |

### 1.2. 基本信息

#### 1.1.1. API基本信息

* API Key、secretKey

接口需要商户的API Key,请联系商务进行申请，申请成功后，会同时创建用户的secretKey，用于接口签名。本平台不保留secretKey， 需要商户自己妥善保存。

* 本文档，所有接口的请求、响应的数据格式全部采用JSON格式；
* 本文档，所有接口的测试环境格式 <https://baseUrl/webhook/global/bizType>
  * 其中bizType为资源信息，请参考权限说明。
  * baseUrl 为请求的域名或IP，请联系运营人员提供
* 所有时间，包括请求、响应，均为时间戳（UNIX时间），单位为**毫秒**。
* 测试环境地址：<https://pre-api-test.cmfbl.com/webhook>
* 正式环境地址：<https://api.multimarkets.org/webhook>

#### 1.1.2. HTTP响应状态码

* HTTP 4XX 错误码用于指示错误的请求内容、行为、格式。问题在于请求者。
* HTTP 403 错误码表示违反WAF限制(Web应用程序防火墙)。
* HTTP 429 错误码表示警告访问频次超限，即将被封IP。
* HTTP 418 表示收到429后继续访问，IP被封。
* HTTP 5XX 错误码用于指示本平台服务侧的问题。

#### 1.1.3. 访问限制

* 目前单个API-KEY 1分钟访问次数不超过100次，否则将返回429
* 如果返回429后继续访问，即返回418
* 首次返回418时，会被禁用5分钟，再次访问会被禁用10分钟，以此类推。

### 1.2. 请求方式与鉴权

#### 1.2.1 请求头

请求相关通用参数需要放在http请求头，请求头参数如下：

请求头说明参数说明：

| 参数名称       | 说明                   | 数据类型 | 是否必填 |
| ---------- | -------------------- | ---- | ---- |
| apiKey     | 通过平台申请的api\_key      | 字符串  | 必填   |
| recvWindow | 时间空窗，含义见下文《请求时间安全限制》 | 整数   | 非必填  |
| timestamp  | 请求发送的时间（时间戳格式,毫秒）    | 整数   | 必填   |
| signature  | 签名，签名计算方法见下文《签名》。    | 字符串  | 必填   |
| companyId  | 公司ID                 | 整数   | 必填   |
| trace      | 全局链路唯一标志             | 字符串  | 必填   |
| version    | 资源版本号                | 字符串  | 非必填  |
| group      | 资源分组                 | 字符串  | 非必填  |
| lang       | 语言信息，默认zh-CN         | 字符串  | 非必填  |

注意： API-keys 与 secretKey **是大小写敏感的；** 签名，是**大小写敏感的**

#### 1.2.2 签名

签名使用SHA1WithRSA算法. secretKey作为 SHA1WithRSA 的密钥，param中对应的参数 + timestamp 作为SHA1WithRSA的操作对象，得到的输出即为签名。 具体计算方法，见本章节的《示例》。

#### 1.2.3 请求时间安全限制

* 服务器收到请求时，会判断请求参数timestamp，与服务器当前时间比较，如果是5000毫秒（这个时间，称为时间空窗，默认值5000）之前发出的，则拒绝请求。
* 这个时间空窗值，可以通过发送可选参数 recvWindow来自定义。

逻辑伪代码如下:

```
if (timestamp < (serverTime ) && (serverTime - timestamp) <= recvWindow){
 // process request
}else{
 // reject request
}
```

#### 1.2.4. 签名示例

***Api Key***、***secretKey***

```
companyId：439
apiKey：1710e1f6b4b54c15bea72e8669966591
secretKey：MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAI5WJjsBgtiuQQZDs5qe8LBDUm2ZSa4gTBJ+ztq6HkY5P88MqPJuQA8GJ3ZJqAzS8xfFsSfdVDpbFiAjtZYzIbQM0g8e5bER55uGjxLaC2c1BdiHi49KfUdJHwZ1HWoKOtO6NRnNMWo94oBiYSPz4CJSzS2XPDHmGbfo/wOj/IQRAgMBAAECgYAFJAlnciuGpeyFTGatO/ZXd2b/vFyT5Gi69keEUNqNEL5EPSHQ97hqkn9UX16pb+kFv8chOHY1UVrgOEnzrc8Wws6bDb4JbniapUuT4kXZLlA13tO1MNwIxSZoEfajMZ4LUTx7TgDcCYgFhMJrk6dgQQEAsPMNlFy4YWxEV3A9TQJBANPiyCYujM1LN6lITX7HORne12Ns1cHuzZJMu1zMALR5xrktGSmP3kc5gVfmrEgI9YU0dw/I2hSnPcNzW5VogqMCQQCr+HxA88wRV5d28FYwynVA9r982guPj+vnlGfS6jc7cm4YhboSCc12YiZuvloHc676qz2CnH4PR1oCbwVs7v27AkEAqFOhbbPNZ8o5jeJCrlTWqBbARdxQdKCh73fF4RKv/LBBjxqkwr/odezZNFuswg1b/1aOv5twpLe3+W3LdAZywQJBAKVO6XIuaN3Ky0iD4vZnx6q5Bn1nxHEuMeCcoej3SDyW1QoxkhnA3oaL9tHBnR1IsM05SpmBARSCzB1Gx3pdif0CQQCygtbpkypR+dbuj+P9x0ei49IMJZCSB7wlehJtWlRjvK3IFeOaG0lN0ioO9/jET33eo1ekCwvGZDB72FKgcYb/
```

**实际业务请求参数如下**

```
{"companyId":1,"lang":"zh-CN","customerNo":"86001308"}
```

**计算签名：**

**第一步**，获取body里的请求参数，参数按字母排序之后，组成下面的字符串:

```
{"companyId":1,"customerNo":"86001308","lang":"zh-CN"}
```

说明：

* “参数按字母排序”的排序方式为，按照字母从小到大排序，从第一个字母开始比较，一样则比较第二个字母，再一样则比较第三个字母，依此类推。
* 双引号全部去除，无论是字段名，还是字段值的双引号；
* 中间不能有空格；
* 如果字段为null,不参与签名

**第二步**，获取请求头的timestamp的参数，拼接在第一步的结果后面:

```
{companyId:1,customerNo:86001308,lang:zh-CN}1650361143685
```

**第三步**，使用secretKey 通过SHA1WithRSA 对第二步结果进行签名，结果如下 :

```
Dihl6oOt5UkaHo9sEouquP3EqbukLX2dAOoKTSGicYryTvH1m9r6vtSLHGutZn7u34/06gjhdpbXRFPdjb51GVHvG75qWXZ1P/boL89xtuja6eTEy9q/aS8R270Q1A+m/MOTxdiifCy0IByrSpCs4VJKaj2d8jlJo2GHznsH+q0=
```

**附：** **java** **版本** [**demo**](https://github.com/CTradeExchange/sign-demo)**.**

#### 1.2.5 响应格式

采用JSON格式。

所有响应，统一格式：

```
{
    "msg": "描述",
    "fail": true,
    "trace": "请求唯一标识",
    "code": "响应码",
    "data": {},
    "bizCode": "业务代码",
    "tm": 0,
    "msgParams": "异常参数",
    "ok": true
}
```

说明：

* code：依据code判断是否响应成功，code为“0”表示成功；其它均为错误码，见《响应错误码》。
* message: 为code对应的描述信息，比如“请求成功”。
* data 响应的业务数据部分。具体见各个接口。

### 1.3. 通用错误码

| 编码       | 描述             |
| -------- | -------------- |
| 00012001 | 验证签名失败         |
| 00012002 | 请求已超出时间空窗      |
| 00012003 | 请求的API\_KEY不存在 |
| 00012004 | 当前没有对该API的请求权限 |
| 00012005 | 请求过于频繁         |
| 00012006 | API已过期         |
| 00012007 | 非法IP地址         |

说明：具体业务接口错误码参见具体接口说明文档
