接口请求约定
<p>[TOC]</p>
<pre><code># 接口域名 {{base_url}} 全局映射为
1. 正式
https://zowoyoo.malldongli.com
2. 测试
https://dev-zowoyoo.malldongli.com</code></pre>
<h1>一. 请求约定</h1>
<ul>
<li>本文档中所有被动接口都统一使用 HTTP POST 方法调用.</li>
<li>调用参数统一使用JSON, 放置在HTTP的POST请求体中【raw请求体格式】, 接口服务字符编码统一使用UTF-8</li>
<li>特别声明,HTTP链接中的查询参数会被忽略</li>
<li>认证参数放置在HTTP的Header中, 认证参数说明见【接口鉴权(验签)方案】</li>
</ul>
<p>> 示例</p>
<pre><code class="language-text"># url
POST {{base_url}}/api/open_service/ping
# header
version: 1
appid: test_id
timestamp: 1694596590
sign: 258dbcf088894ae21cf97dc5ea4a7c690aa92ac9f9f693d020e2d3023c0fc6cf
# body [raw]
{&quot;hello&quot;:&quot;DongLi&quot;}</code></pre>
<h1>二. 响应示例</h1>
<ul>
<li>相应统一为UTF-8编码的JSON内容</li>
<li>JSON统一的外层框架结构</li>
</ul>
<table>
<thead>
<tr>
<th>字段</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>code</td>
<td>数字</td>
<td>状态码 0:成功 1:失败 其他:见【响应示例】</td>
</tr>
<tr>
<td>message</td>
<td>string</td>
<td>结果说明</td>
</tr>
<tr>
<td>data</td>
<td>不定</td>
<td>接口响应</td>
</tr>
</tbody>
</table>
<h1>三. 响应业务状态码表</h1>
<table>
<thead>
<tr>
<th>状态码</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>成功</td>
</tr>
<tr>
<td>1</td>
<td>通用失败状态. 响应见message</td>
</tr>
<tr>
<td>1000</td>
<td>请求参数有误.</td>
</tr>
<tr>
<td>1001</td>
<td>appid错误/appid禁用</td>
</tr>
<tr>
<td>1002</td>
<td>当前请求, 时间参数不合法.</td>
</tr>
<tr>
<td>1003</td>
<td>验签失败</td>
</tr>
<tr>
<td>1004</td>
<td>版本错误</td>
</tr>
<tr>
<td>1005</td>
<td>请求参数需放在POST的body消息体raw格式</td>
</tr>
<tr>
<td>1006</td>
<td>完全加密, 请求参数消息体raw参数有误</td>
</tr>
<tr>
<td>1007</td>
<td>获取机构信息失败, 请联系管理员.</td>
</tr>
</tbody>
</table>
<h1>四. 接口鉴权(验签)方案</h1>
<p>> 接口统一使用 hash sign 签名方式进行接口鉴权。</p>
<h2>1.鉴权物料说明</h2>
<p>> 【1】开发方在对接前, 需要在东犁方获得接口鉴权使用的 version, appid, appkey, corpid[此值, 在完全加密时使用]。
> 【2】接口对接方有义务保护其 appId, appKey 不外泄。 如因对接方泄漏 appid, appkey, 接口被冒名调用导致的数据泄漏、损坏等问题,东犁方有权拒绝承担修复、赔偿责任。</p>
<h2>2. 鉴权 sign 签名计算说明</h2>
<p>> 使用 SHA256 签名的方式进行接口鉴权。调用方和接口内部使用相同的方法.
> 相同的version, appid和appkey进行签名,比较传入签名与计算签名是否一致来确定是否鉴权通过。</p>
<h2>3. 鉴权 sign 签名计算方式</h2>
<h3>a. 物料准备</h3>
<pre><code class="language-json">// 1. 请求链接 : {{base_url}}/api/open_service/ping
// 2. version : 1
// 3. appid : test_id (仅作示例,实际通常appid要复杂的多)
// 4. appkey : test_key (仅作示例,实际通常appkey要复杂的多)
// 5. 请求调用的消息体 【注意此示例消息体无结尾空白。如果有结尾空白也应计入消息体内容中。】
{&quot;hello&quot;:&quot;DongLi&quot;}</code></pre>
<h3>b. 通过以下步骤获得签名数据:</h3>
<p>> [步骤1]
> 获取系统时间戳或从请求 header 中获取传入时间戳(毫秒数),假设为 1694596594123
> <strong>(请求时间戳, 必须为当前请求时间的前后15秒内合法)</strong></p>
<hr />
<p>> [步骤2]
> 测试环境 : 将[appid]、[version]、[timestamp]、[appkey]联接成一个字符串。此示例结果为:
> 正式环境 : 将[appid]、[version]、[timestamp]、[appkey]、[请求体]联接成一个字符串。此示例结果为:</p>
<pre><code>// appid : test_id
// version : 1
// timestamp : 1694596594123
// appkey : test_key
// 请求体 : {&quot;hello&quot;:&quot;DongLi&quot;}
测试环境 : test_id11694596590test_key
正式环境 : test_id11694596590test_key{&quot;hello&quot;:&quot;DongLi&quot;}</code></pre>
<hr />
<p>> [步骤3]
> 将[步骤2]结果按UTF-8编码转换为字符串数据</p>
<hr />
<p>> [步骤4]
> 对[步骤3]得到的数据计算其SHA256摘要值。此示例结果为:</p>
<pre><code>测试环境 : 258dbcf088894ae21cf97dc5ea4a7c690aa92ac9f9f693d020e2d3023c0fc6cf
正式环境 : fa2dacbd5fac37c189c373bcc6bbbb59cac94cc469935e11ecc89ef54442730e</code></pre>
<hr />
<h3>c. 请求方计算完签名, 拼凑指定参数作为请求Header参数</h3>
<pre><code># header参数包含以下参数
# 需要将[appid]、[version]、[timestamp]、[sign]四个参数, 作为header头的请求内容
appid: test_id
version: 1
timestamp: 1694596590
sign: 258dbcf088894ae21cf97dc5ea4a7c690aa92ac9f9f693d020e2d3023c0fc6cf</code></pre>
<h3>d. 请求返回值</h3>
<pre><code class="language-json">// 请求有误的返回示例
{
&quot;code&quot;: 1007,
&quot;message&quot;: &quot;获取机构信息失败, 请联系管理员.&quot;,
&quot;data&quot;: []
}
{
&quot;code&quot;: 1003,
&quot;message&quot;: &quot;验签失败&quot;,
&quot;data&quot;: []
}
// 请求成功的返回示例 (返回此格式, 表示验签正常.)
{
&quot;code&quot;: 0,
&quot;message&quot;: &quot;成功&quot;,
&quot;data&quot;: {
&quot;headers&quot;: {
&quot;Content-Type&quot;: &quot;application/json;charset=UTF-8&quot;,
&quot;Content-Length&quot;: &quot;19&quot;,
&quot;appid&quot;: &quot;test_id&quot;,
&quot;timestamp&quot;: &quot;1694596590&quot;,
&quot;sign&quot;: &quot;258dbcf088894ae21cf97dc5ea4a7c690aa92ac9f9f693d020e2d3023c0fc6cf&quot;
},
&quot;params&quot;: &quot;&quot;,
&quot;body&quot;: {
&quot;hello&quot;: &quot;DongLi&quot;
}
}
}</code></pre>
<h1><strong>[注] 以下内容, 除在接口详情中特别标注, 否则均不会使用完全加密!</strong></h1>
<h1>五. 完全加密约定</h1>
<h2>1. 完全加密约定说明</h2>
<ul>
<li>部分数据接口, 为了防止数据泄露. [请求消息体] 及 [返回消息体]. 采用完全加密. 加密方式为AES加密(AES/CTR/PKCS5Padding), 加密AEC密钥使用appid生成, CTR分组模式初始向量IV由corpid生成.</li>
<li>为保证消息可以正常解密,消息头中将包含appid信息。接收方可以通过appid确认是否自己持有对应appkey</li>
<li>消息头中同样包含timestamp信息和sign值, sign值的计算参见鉴权 sign 签名计算方式章节.</li>
<li>消息体解密后为json格式. 请求物料使用UTF-8编码</li>
</ul>
<h2>2. [加密/解密] 物料准备</h2>
<h3>a. 使用以下json(结尾无换行)按UTF-8编码, 作为加密数据的物料.</h3>
<pre><code class="language-json">{&quot;hello&quot;: &quot;DongLi&quot;}</code></pre>
<h3>b. AEC密钥生成流程</h3>
<ul>
<li>第一步. 取用appkey按UTF-8编码字节数据 [已经是UTF-8的, 则不需要转码]</li>
<li>第二步. 使用SHA256方式对第一步得到数据计算摘要值 (字节数据)</li>
<li>第三步. 取用第二步结果字节数据前16字节(即16进制字节数据的前32个字符), 作为AEC密钥的字节数据 <strong>[注:1个字节=2个16进制字符]</strong></li>
<li>第四步. 取用第三步结果, 转换十六进制字符串为二进制, 恢复出AEC密钥(将十六进制的16字节, 32个字符, 转为二进制)</li>
</ul>
<pre><code># 举例appkey为hello
# 第一步: hello
# 第二步: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
# 第三步: 2cf24dba5fb0a30e26e83b2ac5b9e29e
# 第四步: 即为最终值</code></pre>
<h3>c. CTR分组模式初始向量IV生成流程</h3>
<ul>
<li>第一步. 取corpid按UTF-8编码字节数据</li>
<li>第二步. 使用SHA256方式对第一步得到数据计算摘要值 (字节数据)</li>
<li>第三步. 取用第二步结果字节数据前16字节(即16进制字节数据的前32个字符), 作为IV字节数据 <strong>[注:1个字节=2个16进制字符]</strong></li>
<li>第四步. 取用第三步结果, 转换十六进制字符串为二进制, 恢复出IV(将十六进制的16字节, 32个字符, 转为二进制)</li>
</ul>
<pre><code># 举例corpid为dongli
# 第一步: dongli
# 第二步: 345f1dc1c1d664da09bd137889e73490d4299a9099d00464f5ae1999bf6bb9b6
# 第三步: 345f1dc1c1d664da09bd137889e73490
# 第四步: 即为最终值</code></pre>
<h3>d. sign值生成逻辑</h3>
<ul>
<li>参考鉴权 sign 签名计算方式章节</li>
</ul>
<h2>3. 加密数据流程</h2>
<p>> 第一步: 使用准备好的[AEC密钥] 和 [向量IV] 对加密物料的字节数据做AES/128位/CTR/PKCS5Padding加密,得到加密后字节数据
> 第二步: 将第一步得到的字节数据按Base64编码,得到Base64编码字符串,即加密后用于传输的数据</p>
<pre><code>此例:
加密物料: {&quot;hello&quot;: &quot;DongLi&quot;}
appkey: hello
corpid: dongli
最终加密后值:
k+xwYLkTL22XXh/TeQ3Y/pOONw==</code></pre>
<h2>4. 解密数据流程</h2>
<p>> 第一步: 将收到的消息体字符串按Base64进行解码,得到加密字节数据
> 第二步: 使用准备好的[AEC密钥] 和 [向量IV] 对第一步得到的加密字节数据做AES/128位/CTR/PKCS5Padding解密,得到加密前数据
> 第三步: 将第二步得到的加密前数据, 使用UTF-8编码恢复字符串数据. 恢复的结果为json字符串</p>
<pre><code>此例:
解密物料: k+xwYLkTL22XXh/TeQ3Y/pOONw==
appkey: hello
corpid: dongli
最终解密后值:
{&quot;hello&quot;: &quot;DongLi&quot;}</code></pre>