A-A+

HTTP通信安全-身份验证

2016年11月13日 技术, 默认 暂无评论 阅读 1,935 次

无论是使用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加密: 

  1. import java.security.MessageDigest;
  2. public class SHA1Util {
  3.     public static String encode(String decript) throws Exception {
  4.             MessageDigest digest = MessageDigest.getInstance("SHA-1");
  5.             digest.update(decript.getBytes());
  6.             byte messageDigest[] = digest.digest();
  7.             // Create Hex String
  8.             StringBuffer hexString = new StringBuffer();
  9.             // 字节数组转换为 十六进制 数
  10.             for (int i = 0; i < messageDigest.length; i++) {
  11.                 String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
  12.                 if (shaHex.length() < 2) {
  13.                     hexString.append(0);
  14.                 }
  15.                 hexString.append(shaHex);
  16.             }
  17.             return hexString.toString();
  18.     }
  19. }

生成加密串:

  1. import com.xiaoleilu.hutool.StrUtil;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. public class SignUtil {
  6.     public static String generate(String appid, String token, long millis) throws Exception {
  7.         String timestamp = String.valueOf(millis);
  8.         String signature = null;
  9.         if (StrUtil.isNotBlank(token) && StrUtil.isNotBlank(timestamp)
  10.                 && StrUtil.isNotBlank(appid)) {
  11.             List<String> srcList = new ArrayList<String>();
  12.             srcList.add(timestamp);
  13.             srcList.add(appid);
  14.             srcList.add(token);
  15.             // 按照字典序逆序拼接参数
  16.             Collections.sort(srcList);
  17.             Collections.reverse(srcList);
  18.             StringBuilder sb = new StringBuilder();
  19.             for (int i = 0; i < srcList.size(); i++) {
  20.                 sb.append(srcList.get(i));
  21.             }
  22.             signature = SHA1Util.encode(sb.toString());
  23.             srcList.clear();
  24.         }
  25.         return signature;
  26.     }
  27. }

如果希望更加安全的话,可以对需要传输的数据进行加密;还可以对需要传输的数据进行MD5加密,将MD5加密所得到的字符串加入到签名之中,这样可以保证数据合法有效。

另外,这种生成签名验证合法性的方式会比较慢,在应用中不太可能每一次都要重复此过程,一般都会在第一次进行身份验证时使用此方式来获得一个身份凭证,该凭证具有一定的有效期,过期后需要再次申请,在有效期内可以使用该凭证进行验证,提高程序运行效率,例子其实可以参见微信SDK。

给我留言

Copyright © 字痕随行 保留所有权利.   Theme  Ality 京ICP备14039894号

用户登录

分享到: