接口签名规则和Java实现签名和验签代码
接口签名规则和Java实现签名和验签代码
签名规则
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证接口调用传送的sign参数不参与签名,将生成的签名与该sign值作校验。
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 注意:密钥的长度为32个字节。
package com.example.core.mydemo.sign; import com.example.core.mydemo.MD5; import org.apache.commons.lang3.StringUtils; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class SignatureTest { /** * 签名 * @param map * @param key * @return * @throws Exception */ public static String getSign(Map<String,String> map,String key) throws Exception{ ArrayList<String> list = new ArrayList<String>(); for(Map.Entry<String,String> entry:map.entrySet()){ if(entry.getValue() != null && StringUtils.isNotBlank(entry.getValue().toString()) && !"null".equals(entry.getValue()) && !"class".equals(entry.getKey()) && !"data".equals(entry.getKey())){ list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); String [] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort); StringBuilder sb = new StringBuilder(); for(int i = 0; i < size; i ++) { sb.append(arrayToSort[i]); } String result = sb.toString(); //过滤最后一个字符串& int lastIdx = result.lastIndexOf("&"); result = result.substring(0,lastIdx); result += key; try{ result = MD5.MD5Encode(result).toUpperCase(); }catch (Exception e) { e.printStackTrace(); } return result; } /** * 验证签名 * @param map * @param key * @return * @throws Exception */ public static boolean checkIsSignValidFromResponseString(Map<String,String> map,String key) throws Exception { String signFromAPIResponse = null; if(map.get("sign")!=null){ signFromAPIResponse = map.get("sign").toString(); } if(signFromAPIResponse=="" || signFromAPIResponse == null){ return false; } map.put("sign",""); map.put("class",""); //重新签名 String signForAPIResponse = SignatureTest.getSign(map,key); if(!signForAPIResponse.equals(signFromAPIResponse)){ //签名验不过,表示这个API返回的数据有可能已经被篡改了 return false; } return true; } public static void main(String[] args) { try { //key没有位数的要求 String key = "1234567890tkltktqVdTstvuhlZHTest"; //simple test Map<String,String> map = new HashMap<String,String>(); map.put("appId", "0001"); map.put("userName", "steve"); map.put("pwd", "123456"); map.put("sex", "man"); String sign = SignatureTest.getSign(map,key); System.out.println(sign); map.put("sign", sign); //验证签名 System.out.println(SignatureTest.checkIsSignValidFromResponseString(map,key)); } catch (Exception e) { e.printStackTrace(); } } }
package com.example.core.mydemo; import java.security.MessageDigest; public class MD5 { private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; /** * 转换字节数组为16进制字串 * @param b 字节数组 * @return 16进制字串 */ public static String byteArrayToHexString(byte[] b) { StringBuilder resultSb = new StringBuilder(); for (byte aB : b) { resultSb.append(byteToHexString(aB)); } return resultSb.toString(); } /** * 转换byte到16进制 * @param b 要转换的byte * @return 16进制格式 */ 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]; } /** * MD5编码 * @param origin 原始字符串 * @return 经过MD5加密之后的结果 */ public static String MD5Encode(String origin) { String resultString = null; try { resultString = origin; MessageDigest md = MessageDigest.getInstance("MD5"); resultString = byteArrayToHexString(md.digest(resultString.getBytes())); } catch (Exception e) { e.printStackTrace(); } return resultString; } }