首先是背景實作的接口驗簽,這裡放上我的代碼位址
貼上核心代碼
public class SignUtil {
private static final String FIELD_SIGN = "sign";
private static final String FIELD_SIGN_TYPE = "sign_type";
private static final String TIMESTAMP = "timestamp";
private static final String APP_KEY = "appKey";
/**
* 預設逾時時間
*/
private static final Long DEFAULT_TIME_OUT = 5000L;
private static String md5(String data) {
return SecureUtil.md5(data);
}
private static String hmacSha256(String data, String key) {
return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, key).digestHex(data, CharsetUtil.UTF_8);
}
/**
* 建構簽名 Map
*
* @param appKey appKey
* @param appSecret appSecret
* @param signType {@link SignType} 簽名類型
* @param haveSignType 簽名是否包含 sign_type 字段
* @return 建構簽名後的 Map
*/
public static Map<String, String> createSign(Map<String, String> params, String appKey, String appSecret, SignType signType, boolean haveSignType) {
return buildSign(params, appKey, appSecret, signType, haveSignType);
}
/**
* 建構簽名
*
* @param params 需要簽名的參數
* @param appKey appKey
* @param appSecret 密鑰
* @param signType 簽名類型
* @param haveSignType 簽名是否包含 sign_type 字段
* @return 簽名後的 Map
*/
private static Map<String, String> buildSign(Map<String, String> params, String appKey, String appSecret, SignType signType, boolean haveSignType) {
if (haveSignType) {
params.put(FIELD_SIGN_TYPE, signType.getType());
}
params.put(APP_KEY, appKey);
params.put(TIMESTAMP, String.valueOf(System.currentTimeMillis()));
String sign = createSign(params, appSecret, signType);
params.put(FIELD_SIGN, sign);
return params;
}
/**
* 生成簽名
*
* @param params 需要簽名的參數
* @param appSecret 密鑰
* @param signType 簽名類型
* @return 簽名後的資料
*/
private static String createSign(Map<String, String> params, String appSecret, SignType signType) {
if (signType == null) {
signType = SignType.MD5;
}
// 生成簽名前先去除sign
params.remove(FIELD_SIGN);
String tempStr = createLinkString(params);
String stringSignTemp = tempStr + "&key=" + appSecret;
if (signType == SignType.MD5) {
return md5(stringSignTemp).toUpperCase();
} else {
return hmacSha256(stringSignTemp, appSecret).toUpperCase();
}
}
/**
* 把所有元素排序
*
* @param params 需要排序并參與字元拼接的參數組
* @return 拼接後字元串
*/
private static String createLinkString(Map<String, String> params) {
return createLinkString(params, false);
}
/**
* @param params 需要排序并參與字元拼接的參數組
* @param encode 是否進行URLEncoder
* @return 拼接後字元串
*/
private static String createLinkString(Map<String, String> params, boolean encode) {
return createLinkString(params, "&", encode);
}
/**
* @param params 需要排序并參與字元拼接的參數組
* @param connStr 連接配接符号
* @param encode 是否進行URLEncoder
* @return 拼接後字元串
*/
private static String createLinkString(Map<String, String> params, String connStr, boolean encode) {
return createLinkString(params, connStr, encode, false);
}
private static String createLinkString(Map<String, String> params, String connStr, boolean encode, boolean quotes) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
StringBuilder content = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
// 拼接時,不包括最後一個&字元
if (i == keys.size() - 1) {
if (quotes) {
content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"');
} else {
content.append(key).append("=").append(encode ? urlEncode(value) : value);
}
} else {
if (quotes) {
content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"').append(connStr);
} else {
content.append(key).append("=").append(encode ? urlEncode(value) : value).append(connStr);
}
}
}
return content.toString();
}
/**
* URL 編碼
*
* @param src 需要編碼的字元串
* @return 編碼後的字元串
*/
private static String urlEncode(String src) {
try {
return URLEncoder.encode(src, CharsetUtil.UTF_8).replace("+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
//驗簽
/**
* 校驗 sign
*
* @param params 參數
* @param appSecret appSecret
* @param signType {@link SignType}
* @return {@link Boolean} 驗證簽名結果
*/
public static boolean verifyNotify(Map<String, String> params, String appSecret, SignType signType) {
String sign = params.get("sign");
String localSign = createSign(params, appSecret, signType);
return sign.equals(localSign);
}
/**
* 驗簽 簽名 自帶預設驗簽逾時時間預設5s
*
* @param params 參數
* @param appSecret secret
* @param signType {@link SignType}
* @return {@link Boolean} 驗證簽名結果
*/
public static boolean verifyTimeStampNotify(Map<String, String> params, String appSecret, SignType signType) {
return verifyTimeStampNotify(params, appSecret, signType, DEFAULT_TIME_OUT);
}
/**
* 驗簽 簽名
*
* @param params 參數
* @param appSecret secret
* @param signType {@link SignType}
* @param timeout 逾時時間
* @return {@link Boolean} 驗證簽名結果
*/
public static boolean verifyTimeStampNotify(Map<String, String> params, String appSecret, SignType signType, long timeout) {
String timeStamp = params.get(TIMESTAMP);
Assert.notNull(timeStamp, "{}不能為空", TIMESTAMP);
Assert.state(System.currentTimeMillis() - Long.parseLong(timeStamp) < timeout, "簽名過期");
return verifyNotify(params, appSecret, signType);
}
前台js代碼,這裡用到的md5等代碼,自己百度去找一個插件即可
getSign(obj) {
let that = this;
let param = obj
var timeStamp = new Date().getTime();
if (typeof param == "object") {
if (param != null && param != undefined && param != '') {
var arr = [];
for (var i in param) {
arr.push(i + "=" + param[i]);
}
param = arr.join("&")
}
}
// 判斷是否有參數
if (param != null && param != 'undefined' && param != '') {
param = "appKey=" + appKey + "×tamp=" + timeStamp + "&" + param;
} else {
param = "appKey=" + appKey + "×tamp=" + timeStamp
}
var paramt = param.split("&");
param = paramt.sort().join("&");
var sign = md5(param + "&key=" + appSecret);
var ssss = param + "&sign=" + sign;
// console.log(ssss)
var ti = {};
var keyvalue = [];
var key = "",
value = ""
var paraString = ssss.split("&")
for (var i in paraString) {
keyvalue = paraString[i].split("=");
key = keyvalue[0];
value = keyvalue[1];
ti[key] = value;
}
return ti;
},
注意這裡js生成的sign簽名并沒有轉大寫,背景驗簽的時候直接加上toUpperCase即可。
測試即可