一、数云对外接口技术方案
<p><strong>Chapter 1. 对接数云openapi步骤</strong> </p>
<ul>
<li>1.对接者先向数云申请serviceName和serviceSecret
serviceName: 接口调用服务的身份标识,是一个固定的字符串常量,请向数云申请
serviceSecret:接口调用服务的身份密钥,请向数云申请。为确保安全,请妥善保管,不可泄露。</li>
<li>2.对接者根据API定义编写客户端程序,可以使用任何技术语言编写。</li>
<li>3.对接者与数云数据服务端进行接口联调,直至调试通过。</li>
</ul>
<p><strong>Chapter 2. 接口设计</strong> </p>
<ul>
<li>接口url: /{contextPath}/{version}{requestPath}
其中:
1) contextPath 是被调用服务的名称;
2) version 是被调用服务的版本信息;
3) requestPath是调用服务的请求路径。
示例如下:
url: api.xxx.com/openapi/v2/wechat/member?memberId=5314115615&memberType=tcbj
contextPath: openapi
version: v2
requestPath: /wechat/member</li>
</ul>
<p><strong>Chapter 3. 公共请求消息头</strong> </p>
<ul>
<li>1.所有请求头都必须包含 X-Caller-Service、 X-Caller-Timestamp、 X-Caller-Sign、 Content-Type</li>
<li>2.说明</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">请求头(Header)</th>
<th style="text-align: left;">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">X-Caller-Service</td>
<td style="text-align: left;">对应上文中的serviceName</td>
</tr>
<tr>
<td style="text-align: left;">X-Caller-Timestamp</td>
<td style="text-align: left;">时间戳.格式:yyyy-MM-dd HH:mm:ss,时区为GMT+8.如:2015-01-01 12:20:11</td>
</tr>
<tr>
<td style="text-align: left;">X-Caller-Sign</td>
<td style="text-align: left;">签名. 参考sign安全校验机制</td>
</tr>
<tr>
<td style="text-align: left;">Content-Type</td>
<td style="text-align: left;">只支持JSON格式. application/json; charset=utf-8</td>
</tr>
</tbody>
</table>
<p><strong>Chapter 4. sign安全校验机制</strong> </p>
<ul>
<li>1.各个服务通过gateway进行调用的时候,需要增加三个自定义的头信息:X-Caller-Service、XCaller-Timestamp、X-Caller-Sign.</li>
<li>2.X-CallerSign是通过callerService、timestamp、contextPath、version、serviceSecret、restPath,按首字母排序后,进行md5加密获得的值,只需要将对应的名字做修改即可.
Java版本代码如下:
<pre><code class="language-java">import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class SignConvertUtil {
public static String generateSign(String callerService, String contextPath, String version,String timestamp, String serviceSecret, String requestPath) {
String sign = "";
if (callerService == null || callerService.equals("") || contextPath == null ||contextPath.equals("") ||timestamp == null || timestamp.equals("") || serviceSecret == null || serviceSecret.equals("")) {
return sign;
}
Map<String, String> map = new LinkedHashMap<>();
map.put("callerService", callerService);
map.put("contextPath", contextPath);
try {
if (requestPath != null) {
StringBuilder sb = new StringBuilder();
for(String part : requestPath.split("/")) {
sb.append("/").append(URLEncoder.encode(part,"utf-8"));
}
map.put("requestPath", sb.toString().substring(1));
}
map.put("timestamp", timestamp);
map.put("v", version);
sign = generateMD5Sign(serviceSecret, map);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
return "";
}
return sign;
}
private static String generateMD5Sign(String secret, Map<String, String> parameters) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[]bytes=md5.digest(generateConcatSign(secret,parameters).getBytes("utf-
8"));
return byteToHex(bytes);
}
private static String generateConcatSign(String secret, Map<String, String>parameters) {
StringBuilder sb = new StringBuilder().append(secret);
Set<String> keys = parameters.keySet();
for (String key : keys) {
sb.append(key).append(parameters.get(key));
}
return sb.append(secret).toString();
}
private static String byteToHex(byte[] bytesIn) {
StringBuilder sb = new StringBuilder();
for (byte byteIn : bytesIn) {
String bt = Integer.toHexString(byteIn & 0xff);
if (bt.length() == 1)
sb.append(0).append(bt);
else
sb.append(bt);
}
return sb.toString().toUpperCase();
}
}</code></pre></li>
</ul>
<p><strong>Chapter 5. 响应状态码</strong> </p>
<ul>
<li>1xx:信息,请求收到,继续处理</li>
<li>2xx:成功,行为被成功地接受、理解和采纳</li>
<li>3xx:重定向,为了完成请求,必须进一步执行的动作</li>
<li>4xx:客户端错误,请求包含语法错误或者请求无法实现</li>
<li>5xx:服务器错误,服务器不能实现一种明显无效的请求
每种状态码都有不同含义,具体请参考HTTP状态码:<a href="http://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status" title="HTTP 响应代码">HTTP 响应代码</a></li>
</ul>
<p><strong>Chapter 6. 错误返回格式</strong> </p>
<ul>
<li><code>HTTP状态非2xx时,错误返回统一为如下格式:</code></li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">参数名</th>
<th style="text-align: left;">类型</th>
<th style="text-align: left;">是否必须</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">error_code</td>
<td style="text-align: left;">String</td>
<td style="text-align: left;">是</td>
<td>错误码</td>
</tr>
<tr>
<td style="text-align: left;">msg</td>
<td style="text-align: left;">String</td>
<td style="text-align: left;">是</td>
<td>错误信息</td>
</tr>
</tbody>
</table>
<ul>
<li>
<p><code>示例:</code></p>
<pre><code>{
"error_code" : "opai.invalid-parameter",
"msg" : "参数不合法"
} </code></pre>
</li>
<li><code>错误码说明:</code></li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">错误码</th>
<th style="text-align: left;">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">opai.invalid-parameter</td>
<td style="text-align: left;">参数不合法</td>
</tr>
</tbody>
</table>