feat:外部接口接入
This commit is contained in:
@@ -239,6 +239,7 @@ public enum ErrorCodeEnum {
|
||||
TALLY_BOOK_NOT_EXIST(180001, "记账本数据不存在", null),
|
||||
|
||||
THIRD_API_ERROR(151001,"第三方服务异常->{0}",null),
|
||||
THIRD_API_SIGN_ERROR(151002,"签名失败->{0}",null),
|
||||
;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.cool.store.utils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author suzhuhong
|
||||
* @Date 2025/4/2 9:36
|
||||
* @Version 1.0
|
||||
*/
|
||||
|
||||
public class HmacSigner {
|
||||
|
||||
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||
private static final DateTimeFormatter GMT_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
|
||||
.withZone(ZoneId.of("GMT"));
|
||||
|
||||
/**
|
||||
* 生成与Postman完全一致的签名头
|
||||
*/
|
||||
public static Map<String, String> generateHeaders(
|
||||
String username,
|
||||
String secret,
|
||||
String requestBody) throws Exception {
|
||||
|
||||
// 1. 生成RFC 1123格式时间戳
|
||||
String curDate = ZonedDateTime.now(ZoneId.of("GMT"))
|
||||
.format(GMT_FORMATTER);
|
||||
|
||||
// 2. 计算请求体摘要
|
||||
String computedDigest = computeDigest(requestBody);
|
||||
|
||||
// 3. 构建签名字符串
|
||||
String signingString = "x-date: " + curDate + "\n" + "digest: " + computedDigest;
|
||||
|
||||
// 4. 计算HMAC签名
|
||||
String signature = hmacSha256(secret, signingString);
|
||||
|
||||
// 5. 组装Authorization头
|
||||
String authorization = String.format(
|
||||
"hmac username=\"%s\", algorithm=\"hmac-sha256\", headers=\"x-date digest\", signature=\"%s\"",
|
||||
username,
|
||||
signature
|
||||
);
|
||||
|
||||
// 6. 返回头信息Map
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("x-Date", curDate);
|
||||
headers.put("Digest", computedDigest);
|
||||
headers.put("Authorization", authorization);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private static String computeDigest(String data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
|
||||
return "SHA-256=" + Base64.getEncoder().encodeToString(hash);
|
||||
}
|
||||
|
||||
private static String hmacSha256(String secret, String data) throws Exception {
|
||||
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
||||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM));
|
||||
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,7 @@ import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -21,11 +17,6 @@ import java.util.*;
|
||||
@Slf4j
|
||||
public class SignatureUtils {
|
||||
|
||||
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||
private static final DateTimeFormatter RFC_1123_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
|
||||
.withZone(ZoneId.of("GMT"));
|
||||
|
||||
/**
|
||||
* 生成 HmacSHA256 签名
|
||||
* @param data 待签名字符串
|
||||
@@ -34,8 +25,8 @@ public class SignatureUtils {
|
||||
*/
|
||||
public static String hmacSha256(String data, String secret) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
||||
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
||||
mac.init(secretKey);
|
||||
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
return bytesToHex(hash).toLowerCase();
|
||||
@@ -44,57 +35,6 @@ public class SignatureUtils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成与Postman一致的签名头
|
||||
* @param username API用户名
|
||||
* @param secret API密钥
|
||||
* @param requestBody 请求体JSON字符串
|
||||
* @return 包含所有签名头的Map
|
||||
*/
|
||||
public static Map<String, String> generateHeaders(String username,
|
||||
String secret,
|
||||
String requestBody) {
|
||||
// 1. 生成RFC 1123格式时间戳
|
||||
String date = ZonedDateTime.now(ZoneId.of("GMT")).format(RFC_1123_FORMATTER);
|
||||
|
||||
|
||||
// 2. 计算请求体摘要
|
||||
String digest = "SHA-256=" + calculateDigest(requestBody);
|
||||
|
||||
// 3. 构建签名字符串
|
||||
String signingString = "x-date: " + date + "\n" + "digest: " + digest;
|
||||
|
||||
// 4. 计算HMAC签名
|
||||
String signature = hmacSha256( signingString,secret);
|
||||
|
||||
// 5. 组装Authorization头
|
||||
String authorization = String.format(
|
||||
"hmac username=\"%s\", algorithm=\"hmac-sha256\", headers=\"x-date digest\", signature=\"%s\"",
|
||||
username,
|
||||
signature
|
||||
);
|
||||
|
||||
// 6. 返回所有头信息
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("x-date", date);
|
||||
headers.put("Digest", digest);
|
||||
headers.put("Authorization", authorization);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private static String calculateDigest(String data) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Digest计算失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成待签名字符串(参数按字母排序 + appkey + timestamp)
|
||||
* @param params 请求参数Map(需提前过滤空值)
|
||||
@@ -130,7 +70,4 @@ public class SignatureUtils {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user