From 701070b9546b633aecb056eed3f8208e5191a51d Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 30 Jul 2026 11:35:44 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=94=AF=E6=8C=81?= =?UTF-8?q?openid=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/miniapp/api/WxMaUserService.java | 11 ++++++++ .../miniapp/api/impl/WxMaUserServiceImpl.java | 8 ++++++ .../WxMaUserServiceImplPhoneNumberTest.java | 28 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java index bc8b69a14f..7b382f4706 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java @@ -73,6 +73,17 @@ public interface WxMaUserService { */ WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException; + /** + * 获取手机号信息,并校验手机号获取凭证与用户的绑定关系。 + * + * @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件 + * @param openid 用户openid,传入后微信服务端将校验其与code的绑定关系 + * @return 用户手机号信息 + * @throws WxErrorException . + * @apiNote 该接口用于将code换取用户手机号。 + */ + WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException; + /** * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起 * diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java index c9c7a7b773..e15e500656 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java @@ -67,8 +67,16 @@ public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedDat @Override public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException { + return this.getPhoneNumber(code, null); + } + + @Override + public WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException { JsonObject param = new JsonObject(); param.addProperty("code", code); + if (openid != null) { + param.addProperty("openid", openid); + } String responseContent = this.service.post(GET_PHONE_NUMBER_URL, param.toString()); JsonObject response = GsonParser.parse(responseContent); if (response.has(PHONE_INFO)) { diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java new file mode 100644 index 0000000000..790a7503a3 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java @@ -0,0 +1,28 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Test; + +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.GET_PHONE_NUMBER_URL; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * {@link WxMaUserServiceImpl} 获取手机号接口的单元测试。 + */ +public class WxMaUserServiceImplPhoneNumberTest { + + @Test + public void shouldSendOpenidWhenGettingPhoneNumber() throws WxErrorException { + WxMaService wxMaService = mock(WxMaService.class); + when(wxMaService.post(anyString(), anyString())).thenReturn("{\"phone_info\":{}}"); + + new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", "user-openid"); + + verify(wxMaService).post(GET_PHONE_NUMBER_URL, "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}"); + } +} From e27d3cce5a1e2edb0e953ae3e4f2d507288ba8b4 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 30 Jul 2026 11:59:28 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=8E=A5=E5=8F=A3=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/common/util/DataUtils.java | 6 +++-- .../weixin/common/util/DataUtilsTest.java | 10 ++++++++ .../wx/miniapp/api/WxMaUserService.java | 4 +++- .../miniapp/api/impl/BaseWxMaServiceImpl.java | 2 +- .../miniapp/api/impl/WxMaUserServiceImpl.java | 3 ++- .../WxMaUserServiceImplPhoneNumberTest.java | 24 ++++++++++++++++++- 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java index 095363cf8d..2e9f5d4c7b 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java @@ -17,8 +17,10 @@ public class DataUtils { */ public static E handleDataWithSecret(E data) { E dataForLog = data; - if (data instanceof String && StringUtils.contains((String) data, "secret=")) { - dataForLog = (E) RegExUtils.replaceAll((String) data, "(^|[?&])secret=[^&]*", "$1secret=******"); + if (data instanceof String) { + String stringData = (String) data; + stringData = RegExUtils.replaceAll(stringData, "(^|[?&])secret=[^&]*", "$1secret=******"); + dataForLog = (E) RegExUtils.replaceAll(stringData, "(\\\"openid\\\"\\s*:\\s*\\\")[^\\\"]*(\\\")", "$1******$2"); } return dataForLog; } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java index 1bda61a237..c5088e78c9 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java @@ -47,4 +47,14 @@ public void testHandleDataWithSecretEncodedValue() { assertFalse(s.contains("%2F"), "Encoded characters in the secret must be masked too"); assertTrue(s.contains("&secret=******&"), "Secret should be replaced with asterisks"); } + + @Test + public void testHandleDataWithOpenidInJson() { + String data = "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}"; + + String result = DataUtils.handleDataWithSecret(data); + + assertFalse(result.contains("user-openid")); + assertTrue(result.contains("\"openid\":\"******\"")); + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java index 7b382f4706..65b817f79f 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java @@ -82,7 +82,9 @@ public interface WxMaUserService { * @throws WxErrorException . * @apiNote 该接口用于将code换取用户手机号。 */ - WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException; + default WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException { + return this.getPhoneNumber(code); + } /** * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起 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 9d6c2c0fa6..bf69439a65 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 @@ -374,7 +374,7 @@ public WxMaApiResponse execute( Map headers, String data) throws WxErrorException { - String dataForLog = "Headers: " + headers.toString() + " Body: " + data; + String dataForLog = "Headers: " + headers.toString() + " Body: " + DataUtils.handleDataWithSecret(data); return executeWithRetry( (uriWithAccessToken) -> executor.execute(uriWithAccessToken, headers, data, WxType.MiniApp), uri, diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java index e15e500656..abbb021e8c 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java @@ -16,6 +16,7 @@ import me.chanjar.weixin.common.util.SignUtils; import me.chanjar.weixin.common.util.json.GsonParser; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; import java.util.Map; @@ -74,7 +75,7 @@ public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException { public WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException { JsonObject param = new JsonObject(); param.addProperty("code", code); - if (openid != null) { + if (StringUtils.isNotBlank(openid)) { param.addProperty("openid", openid); } String responseContent = this.service.post(GET_PHONE_NUMBER_URL, param.toString()); diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java index 790a7503a3..610cd13bf8 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java @@ -1,7 +1,10 @@ package cn.binarywang.wx.miniapp.api.impl; import cn.binarywang.wx.miniapp.api.WxMaService; +import com.google.gson.JsonObject; import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.mockito.ArgumentCaptor; import org.testng.annotations.Test; import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.GET_PHONE_NUMBER_URL; @@ -10,6 +13,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; /** * {@link WxMaUserServiceImpl} 获取手机号接口的单元测试。 @@ -23,6 +28,23 @@ public void shouldSendOpenidWhenGettingPhoneNumber() throws WxErrorException { new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", "user-openid"); - verify(wxMaService).post(GET_PHONE_NUMBER_URL, "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}"); + ArgumentCaptor requestBody = ArgumentCaptor.forClass(String.class); + verify(wxMaService).post(eq(GET_PHONE_NUMBER_URL), requestBody.capture()); + JsonObject request = GsonParser.parse(requestBody.getValue()); + assertEquals(request.get("code").getAsString(), "phone-code"); + assertEquals(request.get("openid").getAsString(), "user-openid"); + } + + @Test + public void shouldIgnoreBlankOpenidWhenGettingPhoneNumber() throws WxErrorException { + WxMaService wxMaService = mock(WxMaService.class); + when(wxMaService.post(anyString(), anyString())).thenReturn("{\"phone_info\":{}}"); + + new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", " "); + + ArgumentCaptor requestBody = ArgumentCaptor.forClass(String.class); + verify(wxMaService).post(eq(GET_PHONE_NUMBER_URL), requestBody.capture()); + JsonObject request = GsonParser.parse(requestBody.getValue()); + assertFalse(request.has("openid")); } }