接口簽名工具類

登陸接口簽名方案:
客户端在Header中傳遞時間戳("curTime")、入參的md5值("md5")和校驗和("checkSum")等3個參數;
(1)md5值:
客户端將請求入參使用md5加密並轉成十六進制的字符串形式,放入Header;

(2)校驗和:
客户端將APPSECRET(暫時寫死 "67wGmp7PdhwEp9I4"), md5, curTime等3個參數使用sha1加密並轉成十六進制的字符串形式,放入Header();

注意事項:
入參中的各項參數首字母必須按照字典排序,不能使用格式化後的json, 否則校驗不通過.

sha1密文示例:7c4a8d09ca3762af61e56520943dc26494f8941b
md5密文示例:e10adc3949ba56abbe56e057f20f883e

--------------------------------------------
測試手機號登陸接口示例[已測試通過]:
header部分:
curTime:1532152323277
md5:b580f998587cbfa05ab693581b750a3c
checkSum:485c555005e0664790ff69583cbcb75394f8d4ed

入參部分[注意json不要格式化]:
{"channelType":"9","checkKey":"123456","devId":"87cbfa05ab093581b75","equipmentType":"2","imageCode":"dh59Ds","phone":"13966672478","smsCode":"985264"}

如果簽名校驗失敗會返回:
{
"code": 200,
"errorCode": "549",
"errorMsg": "簽名驗證失敗",
"sid": "6d4a871da5094e4799f0e8cc0b1183461532153216096",
"success": true
}
--------------------------------------------
  1 import com.alibaba.fastjson.JSON;
  2 import com.alibaba.fastjson.serializer.SerializerFeature;
  3 import org.springframework.util.StringUtils;
  4 
  5 import java.security.MessageDigest;
  6 import java.util.HashMap;
  7 import java.util.Map;
  8 
  9 /**
 10  * 簽名工具
 11  *
 12  * @Author: syj
 13  * @CreateDate: 2018/7/20 17:32
 14  */
 15 public class SignUtil {
 16 
 17     /**
 18      * APP密碼
 19      */
 20     private static final String QMALLL_LOGIN_APPSECRET = "DkkGmp3PdhwEp6I8";
 21 
 22     /**
 23      * 校驗簽名
 24      *
 25      * @param curTime     時間戳
 26      * @param requestBody 請求參數
 27      * @param md5         請求參數的md5值
 28      * @param checkSum    校驗和(將appsecret、請求參數md5值和時間戳採用sha1加密)
 29      * @return
 30      */
 31     private boolean doCheck(String curTime, String requestBody, String md5, String checkSum) {
 32         if (StringUtils.isEmpty(md5) || StringUtils.isEmpty(checkSum)) {
 33             return false;
 34         }
 35         String verifyMD5 = SignUtil.md5(requestBody);
 36         String verifyChecksum = SignUtil.checkSum(QMALLL_LOGIN_APPSECRET, verifyMD5, curTime);
 37         return md5.equals(verifyMD5) && checkSum.equals(verifyChecksum) ? true : false;
 38     }
 39 
 40     /**
 41      * 計算並獲取CheckSum
 42      * 使用sha1加密
 43      *
 44      * @param appSecret
 45      * @param nonce
 46      * @param curTime
 47      * @return
 48      */
 49     public static String checkSum(String appSecret, String nonce, String curTime) {
 50         return encode("sha1", appSecret + nonce + curTime);
 51     }
 52 
 53     /**
 54      * 計算並獲取md5值
 55      *
 56      * @param requestBody
 57      * @return
 58      */
 59     public static String md5(String requestBody) {
 60         return encode("md5", requestBody);
 61     }
 62 
 63     public static String encode(String algorithm, String value) {
 64         if (value == null) {
 65             return null;
 66         }
 67         try {
 68             MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
 69             messageDigest.update(value.getBytes());
 70             return getFormattedText(messageDigest.digest());
 71         } catch (Exception e) {
 72             throw new RuntimeException(e);
 73         }
 74     }
 75 
 76     /**
 77      * 把密文轉換成十六進制的字符串形式
 78      *
 79      * @param bytes
 80      * @return
 81      */
 82     private static String getFormattedText(byte[] bytes) {
 83         int len = bytes.length;
 84         StringBuilder buf = new StringBuilder(len * 2);
 85         for (int j = 0; j < len; j++) {
 86             buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
 87             buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
 88         }
 89         return buf.toString();
 90     }
 91 
 92     private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
 93 
 94     /**
 95      * 簽名測試
 96      */
 97     private static void signTest() {
 98         // 請求時間戳
 99         String curTime = String.valueOf(System.currentTimeMillis());
100 
101         // 模擬請求入參
102         Map<String, Object> map = new HashMap<>();
103         map.put("userName", "admin");
104         map.put("password", "123456");
105         map.put("applicationId", 1L);
106         map.put("otherNickName", "系統管理員");
107         map.put("phone", "15801081566");
108         map.put("verificationCode", "123456");
109         map.put("responseType", "1");
110         map.put("osType", "ios");
111         map.put("mac", "123456789");
112         // 轉成json並按字典排序(注意版本:本人使用的fastjson版本為1.2.47)
113         String requestBody = JSON.toJSONString(map, SerializerFeature.MapSortField);
114 
115         // 對請求入參使用md5加密
116         String md5 = SignUtil.md5(requestBody);
117 
118         // 使用sha1加密
119         String checkSum = SignUtil.checkSum(QMALLL_LOGIN_APPSECRET, md5, curTime);
120 
121         //  校驗
122         signTest(curTime, requestBody, md5, checkSum);
123     }
124 
125     /**
126      * 加密測試
127      */
128     private static void md5Test(String data) {
129         String sha1 = SignUtil.encode("sha1", data);
130         String md5 = SignUtil.encode("md5", data);
131         System.out.println("sha1密文:" + sha1);
132         System.out.println("md5密文:" + md5);
133     }
134 
135     /**
136      * 測試簽名
137      *
138      * @param curTime     時間戳
139      * @param requestBody 請求參數
140      * @param md5         請求參數的md5值
141      * @param checkSum    校驗和(將appsecret、請求參數md5值和時間戳採用sha1加密)
142      * @return
143      */
144     public static boolean signTest(String curTime, String requestBody, String md5, String checkSum) {
145         System.out.println("入參:curTime=" + curTime);
146         System.out.println("入參:requestBody=" + requestBody);
147         System.out.println("入參:md5=" + md5);
148         System.out.println("入參:checkSum=" + checkSum);
149         // 計算md5
150         String verifyMD5 = SignUtil.md5(requestBody);
151         // 計算checkSum
152         String verifyChecksum = SignUtil.checkSum(QMALLL_LOGIN_APPSECRET, verifyMD5, curTime);
153 
154         System.out.println("md5值:" + verifyMD5);
155         System.out.println("校驗和:" + verifyChecksum);
156 
157         // 比較md5,checkSum是否一致
158         boolean checkResult = md5.equals(verifyMD5) && checkSum.equals(verifyChecksum) ? true : false;
159         System.out.println("校驗結果:" + checkResult);
160         return checkResult;
161     }
162 
163     /**
164      * 測試
165      *
166      * @param args
167      */
168     public static void main(String[] args) {
169         md5Test("admin");
170         signTest();
171     }
172 
173 }