在http协议中,往往要通过报文头发送一个数据签名,用来确认访问者身份或者检查数据是否是原数据而不是被篡改的数据,我下面说的就是一种对数据进行32位MD5加密制作数据签名的方法:
签名规则:
所有的参数按字母排序,参数名和值用“=”连接,参数之母间加“&”来连接如:
待加密签名字符串: nameA=valueA&nameB=valueB&nameC=valueC&nameD=valueD
步骤:
首先写个实体类来接收保存数据参数:MD5Model.java
public class MD5Model {
private String nameD = null;
private String nameB = null;
private String nameC = null;
private String nameA = null;
public MD5Model(String nameD, String nameB, String nameC, String nameA) {
super();
this.nameD = nameD;
this.nameB = nameB;
this.nameC = nameC;
this.nameA = nameA;
}
@Override
public String toString() {
return "MD5Model [nameD=" + nameD + ", nameB=" + nameB + ", nameC="
+ nameC + ", nameA=" + nameA + "]";
}
public String getNameD() {
return nameD;
}
public void setNameD(String nameD) {
this.nameD = nameD;
}
public String getNameB() {
return nameB;
}
public void setNameB(String nameB) {
this.nameB = nameB;
}
public String getNameC() {
return nameC;
}
public void setNameC(String nameC) {
this.nameC = nameC;
}
public String getNameA() {
return nameA;
}
public void setNameA(String nameA) {
this.nameA = nameA;
}
}
写一个工具类:Md5Util.java
工具类中包含:
获取实体类参数并且给参数按照签名规则排序生成待加密签名字符串方法:
/**
* 获取实体类拼成的加密字段
* @param classA 传入参数实体类
* @return 待加密字符串
*/
public static String getSign(Object classA) {
Field[] fs = classA.getClass().getDeclaredFields();//获取所有属性
String[][] temp = new String[fs.length-1][2]; //用二维数组保存 参数名和参数值
for (int i=0; i<fs.length; i++) {
fs[i].setAccessible(true);
temp[i][0] = fs[i].getName().toLowerCase(); //获取属性名
try {
temp[i][1] = (String) fs[i].get(classA);//把属性值放进数组
} catch (Exception e) {
System.out.println("【签名字段:"+fs[i].getName()+"添加失败】");
}
}
temp = doChooseSort(temp); //对参数实体类按照字母顺序排续
String result = "";
for (int i = 0; i < temp.length; i++) {//按照签名规则生成待加密字符串
result = result + temp[i][0]+"="+temp[i][1]+"&";
}
return result.substring(0, result.length()-1);//消除掉最后的“&”
}
/**
* 对二维数组里面的数据进行选择排序,按字段名按abcd顺序排列
* @param data 未按照字母顺序排序的二维数组
* @return
*/
public static String[][] doChooseSort(String[][] data) {//排序方式为选择排序
String[][] temp = new String[data.length][2];
temp = data;
int n = temp.length;
for (int i = 0; i < n-1; i++) {
int k = i;// 初始化最小值的小标
for (int j = i+1; j<n; j++) {
if (temp[k][0].compareTo(temp[j][0]) > 0) { //下标k字段名大于当前字段名
k = j;// 修改最大值的小标
}
}
// 将最小值放到排序序列末尾
if (k > i) { //用相加相减法交换data[i] 和 data[k]
String tempValue ;
tempValue = temp[k][0];
temp[k][0] = temp[i][0];
temp[i][0] = tempValue;
tempValue = temp[k][1];
temp[k][1] = temp[i][1];
temp[i][1] = tempValue;
}
}
return temp;
}
32位MD5加密方法:
/**
* 对字符串进行32位MD5加密
* @param str 待加密字符串
* @return
*/
public static String EncodeByMD5(String str) {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes("UTF-8"));
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
String md5=new BigInteger(1, md.digest()).toString(16);
//BigInteger会把0省略掉,需补全至32位
return fillMD5(md5);
} catch (Exception e) {
throw new RuntimeException("MD5加密错误:"+e.getMessage(),e);
}
}
private static String fillMD5(String md5){
//如果不够32位则回调自身补零,最后返回32位长度的签名
return md5.length()==32?md5:fillMD5("0"+md5);
}
测试:
public static void main(String[] args) throws Exception{
//生成参数的实体类
MD5Model md5Model = new MD5Model("valueD","valueB","valueC","valueA");
System.out.println("实体类数据:" + md5Model.toString());
System.out.println("实体类转化成的待加密的签名字符串:" + Md5Util.getSign(md5Model));
System.out.println("加密后的签名字符串:" + Md5Util.EncodeByMD5(Md5Util.getSign(md5Model)));
}
结果:
实体类数据:MD5Model [nameD=valueD, nameB=valueB, nameC=valueC, nameA=valueA]
实体类转化成的待加密的签名字符串:namea=valueA&nameb=valueB&namec=valueC&named=valueD
加密后的签名字符串:c4b040eb23ce28d1cc45f771099cb3f6