天天看點

前後端分離。前端實作參數簽名,後端實作接口驗簽

首先是背景實作的接口驗簽,這裡放上我的代碼位址

貼上核心代碼

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 + "&timestamp=" + timeStamp + "&" + param;
		} else {
			param = "appKey=" + appKey + "&timestamp=" + 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即可。

測試即可