App版:每次请求都需要携带 appId参数(针对所有接口:登录、未登录接口),所有请求都会经过加密签名校验流程,数据 HTTP / HTTPS加密传输。
Pc版:每次请求无需登录的接口没有参数限制要求,需登录的接口必须传userId、token,token为草根金融后台生成,与XX平台的token无关,数据传输为明文。
共同点:两个版本的接口只在mvc层不同,业务逻辑代码为同一套。所有接口均为无状态,网页端、app端需根据接口返回的结果判断用户当前是否登录。所有请求都要带上 accessFrom 参数
从上至下,按环节说明:
appId:
app设备的唯一标识符,由服务端生成,app每次发送请求时,都需要带上此参数。
appSecret
当前设备的通信私钥,由服务端生成
app端需要同时缓存 appId 、appSecret两个字段,appId、appSecret在服务端会有过期时间,2 种情况下需求重新获取:
一、 服务端返回 secret_pass_time_error字符串时,app端在得知私钥过期,此时需要重新获取并缓存。
二、 App刚启动时
接口地址:
/XXXX/secret_key/generate.json
返回值:
{"data":{"appId":"0c008f12901c4cd2a689635b8f6de0eepe9il7g0e27hfmir","appSecret":"f9f731c1cfc04bc48cd1b9b631709949"},"success":true}
请求成功后,app端需缓存appId、appSecret,并在以后的请求中始终带上appId字段。
注:
在调用该接口时,由于appSecret未生成,所有全程使用公钥加密,并且在以后其他接口调用中,对于 appId字段,统一使用公钥加密、解密。
加密算法
Aes + base64
Javaapp端代码示例:
// 用于保存请求的参数名
List
// 最终发送到服务器的参数键值对
Map
if (appId !=null) {
requestMap.put(AppIdKey,appId);
keys.add(AppIdKey);
}
/** 加密业务参数(私钥) */
for (Iterator
String paramKey = it.next();
// 不对appId进行私钥加密
if (paramKey.equals(AppIdKey)) {
continue;
}
keys.add(paramKey);
String encodeValue = EncryptUtil.aesEncode(param.get(paramKey),appSecret);
requestMap.put(paramKey,encodeValue);
}
/** md5签名(私钥) */
// 先对参数名正序排序
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
// 所有参数值相加
sb.append(requestMap.get(key));
}
// 最后加上私钥
sb.append(appSecret);
requestMap.put("sign", Md5Encrypt.md5(sb.toString()));
/** 全局加密(公钥)(包含已用私钥加密过的参数值、未加密的参数名以及appId) */
Map
for (Map.Entry
encrptyParam.put(EncryptUtil.aesEncode(entry.getKey(),publicSecret), EncryptUtil.aesEncode(entry.getValue(),publicSecret));
}
/** 发送请求 */
String result = HttpRequestUtil.httpPostAccess(ApiAddress +uri, encrptyParam,"utf-8");
/** 缓存的appSecret过期,需求重新生成appId、appSecret后再发请求 */
if ("secret_pass_time_error".equals(result)) {
throw new AppScretTimeOutException("密钥过期");
}
/* 解密响应数据(私钥) */
result = EncryptUtil.aesDecode(result,appSecret);