我们在做APP开发的时候,APP的网络安全是极其重要,我们有必要对请求的API进行加密和防篡改。HTTPS是一个很好的传输加密的方式。如果APP的请求API地址和参数被泄露,我们还是可能会被恶意请求。
所以我们有必要实现 URL签名,对请求的参数进行校验,在客户端生成URL签名,在服务端对签名进行校验,如果客户端的URL签名算法保密做得好,就可以避免非法请求,签名算法的加密需要使用 C++编写,这里就不介绍,只介绍URL签名算法的实现。
原理
在 APP 端,对请求的参数+时间戳+密钥,进行MD5生成一个值,并和时间戳、参数一起传到服务器,在服务器也进行相同的方式生成一个MD5,对比两个MD5是否一致,如果一致就说明这个请求是从APP发起的,否则就是非法请求。
算法
算法的实现有很多方式,这里介绍一个比较通用的算法。假设参与参数签名计算的请求参数分别是“k1”、“k2”、“k3”,它们的值分别是“v1”、“v2”、“v3”,则参数签名计算方法如下:
- 每一个请求都必须带有timestamp参数,长度为10的时间戳;
- 将请求参数格式化为“key=value”格式,即“k1=v1”、“k2=v2”、“k3=v3”;
- 将格式化好的参数键值对以字典序升序排列后,拼接在一起,即“k1=v1k2=v2k3=v3”;
- 在拼接好的字符串末尾追加上一个密钥(需要注意保密,保存在服务端和客户端);
- 对上述字符串进行MD5,即为签名的值,并在。
- 在 HTTP Header 增加字段 sign,传递上述的MD5值。
服务器验证
- 对所有接收到的参数也以上面的方式拼接,并加上密钥生成MD5;
- 取出 Header中的 sign 字段进行对比。如果一样就说明没有被篡改,如果不一样就返回错误;
- 对参数中的 timestamp 字段和服务器的时间进行对比,如果误差超过 5分钟就说明请求已经过期,就应该返回错误。
- 生成的签名需要临时保存校验,不允许URL重复请求。
代码
/**
* 签名生成算法
* @param HashMap<String,String> params 请求参数集,所有参数必须已转换为字符串类型
* @param String secret 签名密钥
* @return 签名
* @throws IOException
*/
public static String getSignature(HashMap<String,String> params, String secret) throws IOException
{
// 先将参数以其参数名的字典序升序进行排序
Map<String, String> sortedParams = new TreeMap<String, String>(params);
Set<Entry<String, String>> entrys = sortedParams.entrySet();
// 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
StringBuilder basestring = new StringBuilder();
for (Entry<String, String> param : entrys) {
basestring.append(param.getKey()).append("=").append(param.getValue());
}
basestring.append(secret);
// 使用MD5对待签名串求签
byte[] bytes = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
bytes = md5.digest(basestring.toString().getBytes("UTF-8"));
} catch (GeneralSecurityException ex) {
throw new IOException(ex);
}
// 将MD5输出的二进制结果转换为小写的十六进制
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex);
}
return sign.toString();
}