From b8792bd8aec66db9dc4714c70597331970d0a473 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:08:56 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20#3916=E3=80=90=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E3=80=91=E4=BF=AE=E5=A4=8D=E5=90=8C=E5=9F=8E=E9=85=8D?= =?UTF-8?q?=E9=80=81=E6=8E=A5=E5=8F=A3RSA=E7=AD=BE=E5=90=8D=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../miniapp/api/impl/BaseWxMaServiceImpl.java | 21 +++++- .../api/impl/WxMaSignaturePayloadTest.java | 71 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java index 47ce08bef..13b4248e2 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java @@ -908,6 +908,23 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH return Base64.getEncoder().encodeToString(data); } + /** + * 构造 RSA 待签名串。 + * + *

根据微信官方 API 签名规范,待签名串格式为:
+ * {@code urlpath\nappid\ntimestamp\npostdata}
+ * 字段之间使用换行符 {@code \n} 分隔,共 4 个字段,末尾无额外回车符。 + * + * @param urlPath 当前请求 API 的 URL,不含 Query 参数 + * @param appId 小程序 AppId + * @param timestamp 签名时的时间戳 + * @param postData 加密后的请求 POST 数据(JSON 字符串) + * @return 拼接好的待签名串 + */ + static String buildSignaturePayload(String urlPath, String appId, long timestamp, String postData) { + return urlPath + "\n" + appId + "\n" + timestamp + "\n" + postData; + } + @Override public String postWithSignature(String url, JsonObject jsonObject) throws WxErrorException { long timestamp = System.currentTimeMillis() / 1000; @@ -962,8 +979,8 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH reqData.addProperty("authtag", base64Encode(authTag)); String requestJson = reqData.toString(); - // 计算签名 RSA - String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson; + // 计算签名 RSA,待签名串格式:urlpath\nappid\ntimestamp\npostdata + String payload = buildSignaturePayload(urlPath, appId, timestamp, requestJson); byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8); RSAPrivateKey priKey; try { diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java new file mode 100644 index 000000000..82f436fe5 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java @@ -0,0 +1,71 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +import org.testng.annotations.Test; + +/** + * 验证同城配送 API 签名 payload 格式的单元测试。 + * + *

直接测试 {@link BaseWxMaServiceImpl#buildSignaturePayload} 生产方法, + * 确保待签名串格式符合微信官方规范:
+ * {@code urlpath\nappid\ntimestamp\npostdata}
+ * 共 4 个字段,字段间以换行符 {@code \n} 分隔,末尾无额外回车符。 + * + * @author GitHub Copilot + * @see + * 微信服务端API签名指南 + */ +public class WxMaSignaturePayloadTest { + + private static final String URL_PATH = + "https://api.weixin.qq.com/cgi-bin/express/intracity/createstore"; + private static final String APP_ID = "wx1234567890abcdef"; + private static final long TIMESTAMP = 1700000000L; + private static final String POST_DATA = "{\"iv\":\"abc\",\"data\":\"xyz\",\"authtag\":\"tag\"}"; + private static final String RSA_KEY_SN = "some_serial_number"; + + /** + * 验证 buildSignaturePayload 返回的待签名串恰好包含 4 个字段, + * 格式为:urlpath\nappid\ntimestamp\npostdata + */ + @Test + public void testPayloadHasExactlyFourFields() { + String payload = + BaseWxMaServiceImpl.buildSignaturePayload(URL_PATH, APP_ID, TIMESTAMP, POST_DATA); + + String[] parts = payload.split("\n", -1); + assertEquals(parts.length, 4, "待签名串应恰好包含 4 个字段(urlpath、appid、timestamp、postdata)"); + assertEquals(parts[0], URL_PATH, "第 1 段应为 urlpath"); + assertEquals(parts[1], APP_ID, "第 2 段应为 appid"); + assertEquals(parts[2], String.valueOf(TIMESTAMP), "第 3 段应为 timestamp"); + assertEquals(parts[3], POST_DATA, "第 4 段应为 postdata"); + } + + /** + * 验证 buildSignaturePayload 返回的待签名串不包含 rsaKeySn。 + * rsaKeySn 应通过请求头 Wechatmp-Serial 传递,而不应出现在签名 payload 中。 + */ + @Test + public void testPayloadDoesNotContainRsaKeySn() { + String payload = + BaseWxMaServiceImpl.buildSignaturePayload(URL_PATH, APP_ID, TIMESTAMP, POST_DATA); + + assertFalse(payload.contains(RSA_KEY_SN), + "待签名串不应包含 rsaKeySn,rsaKeySn 应通过请求头 Wechatmp-Serial 传递"); + } + + /** + * 验证 buildSignaturePayload 返回的待签名串与预期格式完全一致。 + */ + @Test + public void testPayloadMatchesExpectedFormat() { + String expected = URL_PATH + "\n" + APP_ID + "\n" + TIMESTAMP + "\n" + POST_DATA; + String actual = + BaseWxMaServiceImpl.buildSignaturePayload(URL_PATH, APP_ID, TIMESTAMP, POST_DATA); + + assertEquals(actual, expected, "待签名串格式应为:urlpath\\nappid\\ntimestamp\\npostdata"); + } +}