接入网关
开放平台接口接入规则
一、准备工作
接口对接之前,随申行会通过邮件的方式告知如下参数:
- 请求的域名
- 加签的盐值
接入方需要提供:
- 网络请求的出口IP
二、HTTPS请求头说明
参数名 | 含义 | 说明 | 必选 |
---|---|---|---|
X-Sign | 签名内容 | 生成的方式详见 三、签名步骤 | 是 |
X-SignAlgorithm | 签名方式 | 1-sha1 ,2-rsa | 固定值目前传1 |
X-Timestamp | 请求时间 | 格式为yyyyMMddHHmmss的当前时间,仅接受5分钟内的时间 | 是 |
X-MerchantId | 商户身份编号 | MerchantId及盐值会以邮件形式通知 | 是 |
Content-Type | 值:application/json | 是 |
三、签名步骤
- 准备请求体 :必须为json格式,不要添加任何空格和换行,以防验签失败。示例请求体: {"mobile":"13666643085","userId":"68805702089"}
- 拼接明文 :验签明文由请求体的 json 字符串 、 时间戳 (与请求头中X-Timestamp的值一致)、 盐值(邮件提供)的拼接组成,中间不含空格,如{"timestamp":1635490727085,"mobile":"13666643085","userId":"68805702089"}20211029150244ABCDEFG,其中最后的ABCDEFG为盐值;
- 生成签名 :用签名工具类的静态方法对明文加签生成签名,将签名放入头部的X-Sign用以验签,签名工具详见 附录1
四、发送请求
- 按照上述要求设置请求头,用post方法请求对应接口。
- 请求的域名会在邮件中通知,具体请求路径详见各接口说明。
五、响应参数
开放平台收到请求后,会做如下响应,若请求网络正常,https响应状态码会置为200;
注意
若 retCode == 0
,代表请求的业务正常,若 retCode != 0
,则要根据 retMsg
提示信息分析错误原因。
参数名 | 数据类型 | 含义 | 说明 | 必选 |
---|---|---|---|---|
retCode | Integer | 响应编码 | 错误编码定义见 附录2 | 是 |
返回数据 | Object | 返回的数据 | 具体见每个接口的说明文档 | 否 |
retMsg | String | 响应信息 | 具体的错误信息 | 否 |
traceId | String | 流水号 | 失败响应的请求的流水号 | 否 |
六、请求示例
代码请求示例 - 基于 Java8
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Slf4j
public class SSXDemo {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
//在此将待发送的请求体转为JSON字符串
String body = JSON.toJSONString(new JSON());
//merchantId会通过邮件发送
String merchantId = "your-merchantId";
//联调环境、产线环境的盐值会通过邮件发送
String salt = "your-salt";
//联调环境、产线环境的域名会通过邮件发送
String host = "ssx-host-test";
//请求path详见具体接口
String path = "ssx-path-test";
String url = host + path;
//请求头里的时间必须是yyyyMMddHHmmss格式的
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
//加密的原文由请求体、时间、盐值拼接得到,中间不能有空格、换行符等
String signValue = encode(body + time + salt);
Request request = new Request.Builder()
.addHeader("X-Sign", signValue)
.addHeader("X-SignAlgorithm", "1")
.addHeader("X-Timestamp", time)
.addHeader("X-MerchantId", merchantId)
.addHeader("Content-Type", "application/json")
.url(url)
.post(RequestBody.create(MediaType.parse("application/json"), body))
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
//相应求状态码,200表示请求成功
int responseCode = response.code();
//获取相应body的内容
String responseBody = response.body().string();
log.info("url={},heads = {},body ={},responseCode= {},responseBody = {}",
url, request.headers(),body,responseCode, responseBody );
}
public static String encode(String plaintext) throws NoSuchAlgorithmException, UnsupportedEncodingException {
if (StringUtils.isBlank(plaintext)){
throw new IllegalArgumentException ("加签原文不能为空");
}
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(plaintext.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return String.valueOf(buf);
}
}
附录 1 :签名工具类示例
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SHA1TranferUtils {
public static String encode(String str) {
if (null == str || 0 == str.length()) {
return "";
}
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return String.valueOf(buf);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}
}
附录 2:错误码及提示信息
错误码 | 提示信息 | 检查项 |
---|---|---|
-2903001 | 时间戳字段为空 | 检查X-Timestamp属性是否为空 |
-2903002 | 时间戳格式不合法 | 检查X-Timestamp属性是否为yyyyMMddHHmmss格式 |
-2903003 | 请求时间与系统相差太大 | 检查X-Timestamp属性对应的时间比当前时间是否过早或过晚 |
-2903011 | 加密算法类型为空 | 检查X-SignAlgorithm是否为空 |
-2903012 | 加密算法类型错误 | 检查X-SignAlgorithm是否为1 |
-2903013 | 签名为空 | 检查X-Sign属性是否为空 |
-2903014 | 签名位数不对 | 检查X-Sign属性是否正确 |
-2903015 | 验签失败 | 检查X-Sign属性为何不一致(盐值等因素) |
-2903016 | 验签异常 | 检查X-Sign属性为何不可用 |
-2903031 | 未通过IP白名单校验 | 与随申行确认IP白名单是否正确 |
-2903032 | IP被列入黑名单 | 与随申行确认为何IP被加入IP黑名单 |
-2903033 | appId不存在或者白名单为空 | 与随申行确认IP白名单 |
-2903041 | 无权限访问资源 | 与随申行确认是否有对应接口访问权限 |
-2903042 | 缺少该商户请求路径配置 | 与随申行确认是否缺少配置 |
-2903051 | 调用过于频繁 | 检查短时间请求过于频繁 |
-2903100 | 校验未知异常 | 与随申行确认为何校验失败 |
-2903101 | APPID对应的资源不存在 | 与随申行确认是否缺少配置 |
-2903102 | APPID为空 | X-MerchantId属性为空 |