无论是使用Web Service、RESTful或者其它的基于Http协议的交互方案,不可避免的都需要解决通信方面的安全问题,常见的无非就是:
1. 明文传输密码。
2. 重放攻击(相关概念参见《HTTP安全-重放攻击》)。
3. 请求来源非法。
本文通过SHA1算法加/解密相关数据,为客户端和服务端的通信提供一种有效可行的解决办法。以获取用户数据为例,我们希望通过地址http://xxx.com/user/get?id=1获得用户A的相关信息。如果该地址暴露在公网上,我们当然希望只有合法的人能够获得该信息,不合法的人被拒之门外,有恶意的人想要获得该数据需要耗费大量的成本。那么我们可以遵循以下过程去做:
1. 一个appid:是客户端的唯一标识,用来确定请求从哪里来;一个私有秘钥:在客户端和服务端分别存放一份,该秘钥不会在网络上传输;一个时间戳:就是当前时间的毫秒数。
2. 在客户端使用SHA1算法将appid,客户端秘钥,时间戳按照一定顺序加密得到一个字符串。
3. 构造http请求,将appid,时间戳,加密得到的字符串和需要传输的数据发送给服务端。
4. 服务端接收到客户端的请求后,需要:
1) 将时间戳与服务器当前时间作比对,如果超时,则认定该请求非法。
2) 验证appid是否存在,如果存在取得其在服务器端存放的秘钥,如果不存在或者秘钥不存在则该请求非法。
3) 将appid,服务器端秘钥,时间戳按照一定顺序加密,比对加密得到的字符串与客户端请求中的字符串是否相同,如果不同,则该请求非法。
4) 检查appid是否具有权限完成该请求。
5. 服务器端逻辑处理,返回数据。
以下为实现该过程的主要代码:
SHA1加密:
import java.security.MessageDigest;
public class SHA1Util {
public static String encode(String decript) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length()
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
}
}
生成加密串:
import com.xiaoleilu.hutool.StrUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SignUtil {
public static String generate(String appid, String token, long millis) throws Exception {
String timestamp = String.valueOf(millis);
String signature = null;
if (StrUtil.isNotBlank(token) && StrUtil.isNotBlank(timestamp)
&& StrUtil.isNotBlank(appid)) {
List srcList = new ArrayList();
srcList.add(timestamp);
srcList.add(appid);
srcList.add(token);
// 按照字典序逆序拼接参数
Collections.sort(srcList);
Collections.reverse(srcList);
StringBuilder sb = new StringBuilder();
for (int i = 0; i
sb.append(srcList.get(i));
}
signature = SHA1Util.encode(sb.toString());
srcList.clear();
}
return signature;
}
}
如果希望更加安全的话,可以对需要传输的数据进行加密;还可以对需要传输的数据进行MD5加密,将MD5加密所得到的字符串加入到签名之中,这样可以保证数据合法有效。
另外,这种生成签名验证合法性的方式会比较慢,在应用中不太可能每一次都要重复此过程,一般都会在第一次进行身份验证时使用此方式来获得一个身份凭证,该凭证具有一定的有效期,过期后需要再次申请,在有效期内可以使用该凭证进行验证,提高程序运行效率,例子其实可以参见微信SDK。