API签名
系统支持两种API的认证方式:Token认证、AK/SK认证。
-
Token认证:用户使用系统生成的授权Token用作认证令牌,服务端根据Token认证请求是否合法,流程简单,但一旦Token泄露,在Token有效期内Token拥有者可以任意调用API,适合对安全性要求不高的场景。
-
AK/SK认证:服务端根据Access Key Id(AK)/ Secret Access Key(SK)加密的方法来验证某个请求的发送者身份。其中AK标识用户,SK作为对称加密通信的秘钥。AK/SK原理使用对称加解密,能够充分校验发送者的身份和保障数据传输的安全性,防止请求被篡改,适合对安全性要求较高的场景。
AK/SK方式
原理
-
客户端:
构建请求(包含 Access Key);
使用请求内容和使用Secret Access Key计算的签名(Signature);
发送请求(包含Access Key,Signature)到服务端。 -
服务端:
根据发送的Access Key查找数据库得到对应的Secret-Key;
使用同样的算法将请求内容和Secret-Key一起计算签名(Signature)
对比用户发送的签名和服务端计算的签名,两者相同则认证通过,否则失败。
签名生成方式
系统支持本地生成签名和网站简易生成签名两种方式。
签名的内容
生成API与服务编排
(1)Headers中的参数: * X-Auth-Key = "APP Key"; //请求的APP Key。请到「我的API-API调用」页面查看APP Key; * X-Auth-ActionId = "API Id"; //请求API的API Id。每个API拥有唯一的API Id,请到「API管理-API-API详情」中查看API ID; * X-Auth-Timestamp = "X-Auth-Timestamp"; //时间戳; (2)Body中的参数:仅Infields中的参数参与签名(不用输入Infields);其他内容(如PageNo,PageSize)均不参与签名。
示例

在上述示例中,参与签名的内容为:
X-Auth-Key = value1
X-Auth-ActionId = value2
X-Auth-Timestamp = value3
uid = value4
注册API
(1)Headers中的必要参数:X-Auth-Key,X-Auth-ActionId, X-Auth-Timestamp; (2)API的输入参数:包含位于Query 、Headers、Body位置的输入参数。

示例
在上述示例中,参与签名的内容为:
X-Auth-Key = value1
X-Auth-ActionId = value2
X-Auth-Timestamp = value3
prod = value4
本地生成签名
具体流程
1.准备参与签名的字段。
2.将签名字段的KeyValuePair按照Key的字典顺序排列(AaBb-Zz,0-9顺序,即大写字母排在小写字母前,数字排在字母后),然后组装成Key1=Value1&Key2=Value2…&Key n=Value n & APP Secret的字符串(需注意APP Secret放在最后);
3.对生成的字符串进行128位md5算法做信息摘要,并转小写;
4.第3步得到的32位长度的全小写字符串即为生成的签名。
排序示例
* 参与签名的字段:X-Auth-Key = value1;X-Auth-ActionId = value2;X-Auth-Timestamp = value3;prod = value4
* 排序结果:X-Auth-ActionId=value2X-Auth-Key=value1X-Auth-Timestamp=value3prod=value4APP Secret=value5
生成示例
以下是一段 JAVA 代码生成签名的示例,以供用户参考。
public static void main(String[] args) throws Exception {
Map<String,Object> param = new HashMap<>();
String sign = getSign("465f90d77a4a4adb86099f3405cc92a7", param);
System.out.println("md5信息摘要后"+sign);
}
public static String getSign(String appSecret, Map<String,Object> param) throws Exception {
String formattedString = formatSignatureParam(appSecret, param);
System.out.println("原始参数md5前:"+formattedString);
return Hashing.md5().hashBytes(formattedString.getBytes(Charsets.UTF_8)).toString();
}
/**
*
* @param sk app secret
* @param param 参数map,包括header,query和body中的参数
* @return
*/
private static String formatSignatureParam(String sk, Map<String,Object> param) {
if (param == null){
throw new RuntimeException("error param");
}
//申请api时的apiId
param.put("X-Auth-ActionId",5);
//app key
param.put("X-Auth-Key",3);
//当前时间戳,如果时间与服务端时间相差大于10分钟,此次请求将判定为签名错误
param.put("X-Auth-Timestamp",System.currentTimeMillis());
System.out.println("时间戳:"+System.currentTimeMillis());
//使用TreeMap可以自动按照key字典顺序排序
TreeMap<String, String> paramMap = new TreeMap<>();
for (Map.Entry<String, Object> en : param.entrySet()) {
if(null == en.getValue()){
continue;
}
//参数应当均为基本类型
paramMap.put(en.getKey(),en.getValue().toString());
}
Set<Map.Entry<String, String>> entries = paramMap.entrySet();
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String val = entry.getValue();
builder.append(key)
.append("=")
.append(val)
.append("&");
}
return builder.append(sk).toString();
}
public static String getMD5String(String str) {
byte[] bytes = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
bytes = md5.digest(str.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return bytes2Hex(bytes);
}
private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static String bytes2Hex(final byte[] data) {
return bytes2Hex(data, true);
}
public static String bytes2Hex(final byte[] data, final boolean toLowerCase) {
return bytes2Hex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
private static String bytes2Hex(final byte[] data, final char[] toDigits) {
final int l = data.length;
final char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return new String(out);
}