小编针对研发工作有一定年限了,在针对APP安全上有很多种方式,在这里我们针对APP基于MD5签名认证及实现上做个描述。
MD5一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致.由于为不可逆所以我们在针对MD5加密的时候一般通过和APP在接口定义数据上进行一定规则的排序后并进行加密操作.有的时候需要加密的参数较多如果我们每次都手动去添加认证对于时间成本上相对消耗较大,为了减少拼接参数进行验证的所以归纳出一个方法(是否可借鉴参考需根据个人需求而决定).
先说下大体逻辑吧.当我们有很多参数需要进行加密的时候,而且加密顺序只有交互双方知道.这个情况下我们可以通过注解的模式进行确定哪些参数进行加密.然后在签名验证的时候进行解析注解后做比对.目前没有针对顺序做处理,如果有需要可以自行补充.下面先上代码
定义注解方法 EntryValidate 用于在参数中进行定义
package com.aibiancheng.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 加密验证:对需要加密的字段进行验证操作
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EntryValidate {
/**
* 是否进行非空验证 默认验证,如果不需要则填写false
* @return
*/
public abstract boolean isValidateNull() default true;
}
参数案例:TestEntryParams
import com.linli.core.annotation.EntryValidate;
public class TestEntryParams {
@EntryValidate
private String userName;
@EntryValidate
private String userCode;
@EntryValidate
private Byte userSex;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public Byte getUserSex() {
return userSex;
}
public void setUserSex(Byte userSex) {
this.userSex = userSex;
}
}
验证方法: 解析EntryValidate注解 EntryValidateUtil<T>
package com.linli.core.common;
import com.linli.common.utils.ObjectUtil;
import com.linli.core.annotation.EntryValidate;
import com.linli.core.exception.CustomException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* 参数校验:解析EntryValidate注解
*/
public class EntryValidateUtil<T> {
private static Logger logger = LoggerFactory.getLogger(EntryValidateUtil.class);
public Class<T> clazz;
/**
* 构造函数
* @param clazz
*/
public EntryValidateUtil(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 验证枚举类
* @param t
* @param signValue
* @return
*/
public boolean validate(T t,String signValue){
try{
String waitSign = "";
Field[] allFields = clazz.getDeclaredFields();
List<Field> fields = new ArrayList<Field>();
for (Field field : allFields){
if (field.isAnnotationPresent(EntryValidate.class)){
fields.add(field);
}
}
for (int i = 0 ; i <fields.size() ; i ++){
Field field = fields.get(i);
field.setAccessible(true);
EntryValidate entryValidate = field.getAnnotation(EntryValidate.class);
if(entryValidate.isValidateNull()){
if(ObjectUtil.isEmpty(field.get(t))){
throw new CustomException("参数不完整");
}
}
if(i == fields.size()-1){
waitSign += field.getName()+"="+field.get(t);
}else{
waitSign += field.getName()+"="+field.get(t)+"&";
}
}
String sign = MD5SIGN.getMD5Sign(waitSign);
return sign.equals(signValue);
}catch (Exception e){
logger.error("entry validate fail >> e:{}",e);
throw new CustomException("加密校验失败");
}
}
}
MD5签名方法: MD5SIGN
package com.linli.core.common;
import java.security.MessageDigest;
/**
* md5签名认证
*/
public class MD5SIGN {
/**
* 获取MD5签名
* @return
*/
public static String getMD5Sign(String ciphertext){
System.out.println("getMd5Sign:" + ciphertext);
if ("".equals(ciphertext) || null == ciphertext || "".equals(ciphertext.trim())) {
return null;
}
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = ciphertext.getBytes("utf-8");
MessageDigest mdInst = MessageDigest.getInstance("MD5");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
在调用的时候 T替换成自己的对象信息即可
EntryValidateUtil<T> entryValidateUtil = new EntryValidateUtil<T>(T.class);
if(entryValidateUtil.validate(T,sign)){
//验证通过处理逻辑
}
这样在APP签名验证的时候我们就不需要去为了参数的繁琐而浪费时间了,测试运行结果先用MD5生成一个模拟参数信息
getMd5Sign:userName=123456&userCode=123456&userSex=1
624C7999EBC6CE132D44AE32D4DBB0CA
调用我们的方法去进行测试
public static void main(String[] args) {
TestEntryParams entryParams = new TestEntryParams();
entryParams.setUserCode("123456");
entryParams.setUserName("123456");
entryParams.setUserSex((byte)1);
EntryValidateUtil<TestEntryParams> entryValidateUtil = new EntryValidateUtil<TestEntryParams>(TestEntryParams.class);
if(entryValidateUtil.validate(entryParams,"624C7999EBC6CE132D44AE32D4DBB0CA")){
System.out.println("验证通过");
}
}