java,请求防篡改MD5加密sign签名最终解决方案
看完这一篇还没搞明白,记得给我发信息,我去找棵树一头撞死
MD5算法
就是个加密 一个字符串MD5的编码格式加密,无论你那种语言运行,相同字符串加密起来结果都是一样的;## MD5算法
盐
盐就是一个随机字符串,没什么特殊的,sign签名的核心就是,原来的参数对象也好,Map也好,数组也好。一堆参数拼接成一个字符串,。然后再拼接上 加密盐。是的,就是拼接,一点多余的都没有。
再经过MD5编码生成的字符串就叫sign签名,
sign签名
sign来源上边已经说清楚了,生成sign的就是编码,啥特殊的都没有。唯一特殊的就是参数转换的字符串又拼接了一段提前存好的字符串,即加密盐。
核心注意的
这个过程,最需要注意的就是
1、对象参数字符串的拼接方式,用的什么字符间隔的
2、参数字符串拼接加密盐的间隔字符.(有的第三方是无间隔直接拼接)
3、编码加密好的sign,要放回到一开始的参数对象里卖,随请求一同发给第三方,让第三方检验,防被篡改参数
4、对象在拼接字符串的时候一定要有排序,大部分是正序,有一些是倒叙。
下面是参数案例和工具类
参数案例
// An highlighted block
{ //⭐⭐⭐⭐⭐⭐外层需要排序[代码里是正序]
"appId": "168154240975",
"timestamp": "1682653362",
"sku_list": [
{ //⭐⭐⭐⭐⭐⭐外层需要排序[代码里是倒叙]
"sku_id": 6624,
"sku_num": 10
},{//⭐⭐⭐⭐⭐⭐外层需要排序[代码里是倒叙]
"sku_id": 6624,
"sku_num": 10
}
],
"user_name": "邓宝宝",
"user_address": "邓宝宝的家",
"user_realname": "邓大大",
"pay_info": [
{ //⭐⭐⭐⭐⭐⭐外层需要排序[代码里是倒叙]
"amount": 10,
"payTime": "2023-07-18 08:00:00",
"paymentCode": "alipay",
"tradeNo": "10082929392137123"
}
]
}
工具类
声明一点啊,难得发一篇文章。工具列好使了记得来点赞哈!
// An highlighted block
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
@Service
@Slf4j
public class MD5SignUtil {
/*
* post 调用 案例
*/
public String queryOrderPostage(Map<String, Object> parm) {
String shipAreaCode = (String) parm.get("shipAreaCode");
List skuList = (List) parm.get("skuList");
if (null==skuList||null==shipAreaCode||skuList.size()<=0||"".equals(shipAreaCode)){
return null;
}
Integer uuid = UUID.randomUUID().toString().replaceAll("-", "").hashCode();
Map<String, Object> map = new HashMap<>();
map.put("appId", "appid");
map.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
map.put("onceString", String.valueOf(uuid = uuid < 0 ? -uuid : uuid));
map.put("versions", "v2");
map.put("shipAreaCode", shipAreaCode);//
List<Map> sku_list = new ArrayList<>();
for ( Object o : skuList) {
Map<String, Object> sku = (Map<String, Object>) o;
sku_list.add(sku);
}
map.put("sku_list", sku_list);
System.out.println(map.toString());
String sign = createSign(map, "");
map.put("sign", sign);
String s = JSONObject.toJSONString(map);
String result = HttpRequest.post("POST_url")
.header(Header.CONTENT_TYPE, "application/json")
.body(JSONObject.toJSONString(map))
.execute()
.body();
//{"status":"success","code":200,"message":"success","data":{"postage":0}}
JSONObject jsonObject2 = JSON.parseObject(result);
String status = (String) jsonObject2.get("data");
return status;
}
/*
* GET 调用案例
*/
public List<Map> queryGeocoding(String pid) throws UnsupportedEncodingException {
Integer uuid= UUID.randomUUID().toString().replaceAll("-", "").hashCode();
Map<String, Object> map = new HashMap<>();
map.put("area_id",pid);
map.put("appId","appId");
map.put("timestamp",String.valueOf(System.currentTimeMillis() / 1000));
map.put("onceString",String.valueOf(uuid = uuid < 0 ? -uuid : uuid));
map.put("versions", "v2");
String sign = createSign(map, "加密盐"); //生成sign
map.put("sign", sign);
String url = buildUrl("GET_url", map);
String result = HttpRequest.get(url)
.header(Header.CONTENT_TYPE, "application/json")
.execute()
.body();
JSONObject jsonObject2 = JSON.parseObject(result);
List<Object> objectList = (List<Object>) jsonObject2.get("data");
String result1 = objectList.toString();
List<Map> mapList = new ArrayList<>();
mapList = JSON.parseArray(result1, Map.class);
return mapList;
}
/*
* TODO MD5 签名加密 生成 sign 不同平台接口接入需要使用需确认盐的拼接方式
*/
public static String createSign(Map<String, Object> params, String secret_key) {
params.remove("sign");
// 使用HashMap,并使用Arrays.sort排序
String[] sortedKeys = params.keySet().toArray(new String[]{});
Arrays.sort(sortedKeys); // TODO ⭐⭐⭐⭐⭐⭐⭐ 外层倒序⭐⭐⭐⭐⭐
StringBuilder builder = new StringBuilder();
for (String key : sortedKeys) {
if (ObjectUtils.isEmpty(params.get(key))) {
continue;
}
Object object = params.get(key);
if (object instanceof List) {
builder = iterateParmStr(builder, key, object);
}else if (object instanceof Map){
builder = iterateParmStr(builder, key, object);
}else {
builder.append(key).append("=").append(params.get(key)).append("&");
}
}
builder.deleteCharAt(builder.length()-1);
builder.append(secret_key);
System.out.println(builder);
String signValue = SecureUtil.md5(builder.toString()).toUpperCase();
return signValue;
}
/*
* TODO 雅! 雅不可言!
* 此处迭代 内部 map 根据key 倒序
* 供应链的这个 sign签名真是麻烦,最外层正序,里层倒序。
*/
public static StringBuilder iterateParmStr(StringBuilder builder, String name, Object object) {
String oldname =name;
if (object instanceof List) {
List list = (List) object;
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
name = name + "[" + i + "]";
builder = iterateParmStr(builder, name, o);
name=oldname;
}
} else if (object instanceof Map) {
Map<String, Object> map = (Map<String, Object>) object;
String[] mapKeys = map.keySet().toArray(new String[]{});
Arrays.sort(mapKeys,Collections.reverseOrder());// TODO ⭐⭐⭐⭐⭐⭐⭐ 内层正序⭐⭐⭐⭐⭐
for (String key1 : mapKeys) {
Object o = map.get(key1);
name = name + "[" + key1 + "]";
builder = iterateParmStr(builder, name, o);
name=oldname;
}
} else {
builder.append(name).append("=").append(object.toString()).append("&");
}
return builder;
}
/*
* buildUrl TODO 参数构建 getURL 构建
*/
private static String buildUrl(String url, Map<String, Object> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder(url);
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, Object> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (org.apache.commons.lang3.StringUtils.isBlank(query.getKey()) && !org.apache.commons.lang3.StringUtils.isBlank(String.valueOf(query.getValue()))) {
sbQuery.append(query.getValue());
}
if (!org.apache.commons.lang3.StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!org.apache.commons.lang3.StringUtils.isBlank(String.valueOf(query.getValue()))) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(String.valueOf(query.getValue()), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
}