安全性
签名实现
假设现在某 API 需要 3 个业务参数,分别是 k1
, k2
, k3
, 它们的值按顺序对应分别是 v1
, v2
, v3
,接口提供方和调用方协商的密钥为 secretKey
, 那么此接口的签名参数 sign
计算方式为:
- 将请求中所有业务参数 (不包括 sign 和 value 为空的参数) 按字典序升序排列,
k1=v1&k2=v2&k3=v3
形式拼接得到unsignedWithoutKey
- 在第 1 步的得出结果
unsignedWithoutKey
尾部,追加双方约定的secretKey
, 得到unsigned
- 将第 2 步得到的结果
unsigned
进行 MD5 编码(32 位小写形式)再转成HexString
,得到sign
提示
- 第一步中参数拼接时,去除 value 为空的参数
- 第一步中参数拼接时,使用实际参数值【先进行 urlDecode 】
- 参数排序,按字典序升序排列
secretKey
为 SGSDK 平台相关人员提供的 产品集成反馈 中【应用密钥】
项
示例参数
建议
建议签名方法编写完成后,用此 Demo 进行测试
假设某接口双方约定的 secretKey=480ednmfzssqs8jz
,请求参数中业务参数包括 caller=kingsoftgame
,time=1489460391
,extra=
,msg=test space
, 其中包含 value 为空的参数 extra,value 带空格的参数 msg
该接口的 sign 签名计算方式为:
// 第一步
// 参数 extra 的 value 为空,拼接时去除。
// 按 key 字段排序后顺序,[calller,msg,time],
unsignedWithoutKey = "caller=kingsoftgame&msg=test space&time=1489460391";
//第二步
unsigned = unsignedWithoutKey + secretKey ;
// unsigned = "caller=kingsoftgame&msg=test space&time=1489460391480ednmfzssqs8jz"
//第三步
sign = Md5Utils.encode(unsigned);
//sign="857db83778e1c67172ca2c2e9cca1e55";
示例代码
package com.sgxsj.game.docs;
import java.util.Objects;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.security.MessageDigest;
public class JavaSignDemo {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
private static String secretKey = "480ednmfzssqs8jz";
public static void getSign(String sign) {
Map<String, Object> apiParams = new HashMap<>();
apiParams.put("caller", "kingsoftgame");
apiParams.put("msg", "test space");
apiParams.put("extra", "");
apiParams.put("time", "1489460391");
String unSignData = getSignData(apiParams);
System.out.println("unSignData:" + unSignData);
System.out.println("MD5String:" + unSignData + secretKey);
String md5Sign = MD5Encode(unSignData + secretKey);
System.out.println("MD5Encode:" + md5Sign);
boolean result = checkSign(md5Sign, sign);
System.out.println("check sign result:" + result);
}
public static String getSignData(Map<String, Object> params) {
StringBuilder content = new StringBuilder();
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if ("sign".equals(key) || "Sign".equals(key) || "sign_type".equals(key)) {
continue;
}
String value = String.valueOf(params.get(key));
if (Objects.isNull(value) || value.isEmpty()) {
continue;
}
if (i != 0) {
content.append("&");
}
content.append(key).append("=").append(value);
}
return content.toString();
}
public static boolean checkSign(String md5Sign, String requestSign) {
if(!requestSign.equals(md5Sign)){
return false;
} else {
return true;
}
}
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes("UTF-8")));
} catch (Exception ex) {
}
return resultString;
}
public static String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static void main(String[] args) {
String sign = "857db83778e1c67172ca2c2e9cca1e55";
getSign(sign);
}
}