From 62a3931aeeaf57e497df43635738efe8079cb29e Mon Sep 17 00:00:00 2001 From: hywr <33077958+hywr@users.noreply.github.com> Date: Thu, 20 May 2021 14:23:26 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20#2115=20=E4=BC=98=E5=8C=96=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=81=E5=B0=8F=E7=A8=8B=E5=BA=8F=E3=80=81?= =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E7=9A=84access=20token?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=B7=E6=96=B0=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=BE=AA=E7=8E=AF=E9=80=92=E5=BD=92=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cp/api/impl/BaseWxCpServiceImpl.java | 10 ++- .../weixin/cp/api/WxCpBusyRetryTest.java | 2 +- .../cp/api/impl/BaseWxCpServiceImplTest.java | 70 ++++++++++++++++- .../miniapp/api/impl/BaseWxMaServiceImpl.java | 10 ++- .../miniapp/api/impl/WxMaServiceImplTest.java | 40 ++++++++++ .../mp/api/impl/BaseWxMpServiceImpl.java | 10 ++- .../weixin/mp/api/WxMpBusyRetryTest.java | 10 +-- .../mp/api/impl/BaseWxMpServiceImplTest.java | 76 ++++++++++++++++--- 8 files changed, 200 insertions(+), 28 deletions(-) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java index 65ba5dce5..8c7017821 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java @@ -241,7 +241,7 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH int retryTimes = 0; do { try { - return this.executeInternal(executor, uri, data); + return this.executeInternal(executor, uri, data, false); } catch (WxErrorException e) { if (retryTimes + 1 > this.maxRetryTimes) { log.warn("重试达到最大次数【{}】", this.maxRetryTimes); @@ -271,7 +271,7 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH throw new WxRuntimeException("微信服务端异常,超出重试次数"); } - protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + protected T executeInternal(RequestExecutor executor, String uri, E data, boolean doNotAutoRefresh) throws WxErrorException { E dataForLog = DataUtils.handleDataWithSecret(data); if (uri.contains("access_token=")) { @@ -291,9 +291,11 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token this.configStorage.expireAccessToken(); - if (this.getWxCpConfigStorage().autoRefreshToken()) { + if (this.getWxCpConfigStorage().autoRefreshToken() && !doNotAutoRefresh) { log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); - return this.execute(executor, uri, data); + //下一次不再自动重试 + //当小程序误调用第三方平台专属接口时,第三方无法使用小程序的access token,如果可以继续自动获取token会导致无限循环重试,直到栈溢出 + return this.executeInternal(executor, uri, data, true); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java index b8a72add1..34d4065dc 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java @@ -22,7 +22,7 @@ public class WxCpBusyRetryTest { @Override public synchronized T executeInternal( - RequestExecutor executor, String uri, E data) + RequestExecutor executor, String uri, E data, boolean doNotAutoRefresh) throws WxErrorException { log.info("Executed"); throw new WxErrorException("something"); diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java index 22cee8f40..739470a2d 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java @@ -1,13 +1,26 @@ package me.chanjar.weixin.cp.api.impl; import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.error.WxMpErrorMsgEnum; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.cp.api.ApiTestModule; import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import org.mockito.Mockito; +import org.testng.Assert; import org.testng.annotations.Guice; import org.testng.annotations.Test; +import java.io.IOException; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; + import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** *
@@ -35,6 +48,61 @@ public class BaseWxCpServiceImplTest {
 
   @Test
   public void testGetProviderToken() throws WxErrorException {
-    assertThat(this.wxService.getProviderToken("111","123")).isNotNull();
+    assertThat(this.wxService.getProviderToken("111", "123")).isNotNull();
+  }
+
+
+  @Test
+  public void testExecuteAutoRefreshToken() throws WxErrorException, IOException {
+    //测试access token获取时的重试机制
+    WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+    BaseWxCpServiceImpl service = new BaseWxCpServiceImpl() {
+      @Override
+      public Object getRequestHttpClient() {
+        return null;
+      }
+
+      @Override
+      public Object getRequestHttpProxy() {
+        return null;
+      }
+
+      @Override
+      public HttpType getRequestType() {
+        return null;
+      }
+
+      @Override
+      public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+        return "模拟一个过期的access token:" + System.currentTimeMillis();
+      }
+
+      @Override
+      public void initHttp() {
+
+      }
+
+      @Override
+      public WxCpConfigStorage getWxCpConfigStorage() {
+        return config;
+      }
+    };
+    config.setAgentId(1);
+    service.setWxCpConfigStorage(config);
+    RequestExecutor re = mock(RequestExecutor.class);
+
+    AtomicInteger counter = new AtomicInteger();
+    Mockito.when(re.execute(Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
+      counter.incrementAndGet();
+      WxError error = WxError.builder().errorCode(WxMpErrorMsgEnum.CODE_40001.getCode()).errorMsg(WxMpErrorMsgEnum.CODE_40001.getMsg()).build();
+      throw new WxErrorException(error);
+    });
+    try {
+      Object execute = service.execute(re, "http://baidu.com", new HashMap<>());
+      Assert.assertTrue(false, "代码应该不会执行到这里");
+    } catch (WxErrorException e) {
+      Assert.assertEquals(WxMpErrorMsgEnum.CODE_40001.getCode(), e.getError().getErrorCode());
+      Assert.assertEquals(2, counter.get());
+    }
   }
 }
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 7d0d96da9..241d0b37f 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
@@ -207,7 +207,7 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
     int retryTimes = 0;
     do {
       try {
-        return this.executeInternal(executor, uri, data);
+        return this.executeInternal(executor, uri, data, false);
       } catch (WxErrorException e) {
         if (retryTimes + 1 > this.maxRetryTimes) {
           log.warn("重试达到最大次数【{}】", maxRetryTimes);
@@ -238,7 +238,7 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
     throw new WxRuntimeException("微信服务端异常,超出重试次数");
   }
 
-  private  T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+  private  T executeInternal(RequestExecutor executor, String uri, E data, boolean doNotAutoRefreshToken) throws WxErrorException {
     E dataForLog = DataUtils.handleDataWithSecret(data);
 
     if (uri.contains("access_token=")) {
@@ -271,9 +271,11 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
         } finally {
           lock.unlock();
         }
-        if (this.getWxMaConfig().autoRefreshToken()) {
+        if (this.getWxMaConfig().autoRefreshToken() && !doNotAutoRefreshToken) {
           log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
-          return this.execute(executor, uri, data);
+          //下一次不再自动重试
+          //当小程序误调用第三方平台专属接口时,第三方无法使用小程序的access token,如果可以继续自动获取token会导致无限循环重试,直到栈溢出
+          return this.executeInternal(executor, uri, data, true);
         }
       }
 
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
index 4e0a886f7..73343d8e4 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
@@ -2,14 +2,25 @@ package cn.binarywang.wx.miniapp.api.impl;
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
 import cn.binarywang.wx.miniapp.test.ApiTestModule;
 import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.error.WxMpErrorMsgEnum;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
 import org.apache.commons.lang3.StringUtils;
+import org.mockito.Mockito;
+import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -101,6 +112,35 @@ public class WxMaServiceImplTest {
   public void testExecute() {
   }
 
+  @Test
+  public void testExecuteAutoRefreshToken() throws WxErrorException, IOException {
+    //测试access token获取时的重试机制
+    WxMaServiceImpl service = new WxMaServiceImpl() {
+      @Override
+      public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+        return "模拟一个过期的access token:" + System.currentTimeMillis();
+      }
+    };
+    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
+    config.setAppid("1");
+    service.setWxMaConfig(config);
+    RequestExecutor re = mock(RequestExecutor.class);
+
+    AtomicInteger counter = new AtomicInteger();
+    Mockito.when(re.execute(Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
+      counter.incrementAndGet();
+      WxError error = WxError.builder().errorCode(WxMpErrorMsgEnum.CODE_40001.getCode()).errorMsg(WxMpErrorMsgEnum.CODE_40001.getMsg()).build();
+      throw new WxErrorException(error);
+    });
+    try {
+      Object execute = service.execute(re, "http://baidu.com", new HashMap<>());
+      Assert.assertTrue(false, "代码应该不会执行到这里");
+    } catch (WxErrorException e) {
+      Assert.assertEquals(WxMpErrorMsgEnum.CODE_40001.getCode(), e.getError().getErrorCode());
+      Assert.assertEquals(2, counter.get());
+    }
+  }
+
   @Test
   public void testGetWxMaConfig() {
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java
index d340c6dc4..828921b1e 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java
@@ -341,7 +341,7 @@ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestH
     int retryTimes = 0;
     do {
       try {
-        return this.executeInternal(executor, uri, data);
+        return this.executeInternal(executor, uri, data, false);
       } catch (WxErrorException e) {
         WxError error = e.getError();
         // -1 系统繁忙, 1000ms后重试
@@ -370,7 +370,7 @@ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestH
     throw new WxRuntimeException("微信服务端异常,超出重试次数");
   }
 
-  protected  T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+  protected  T executeInternal(RequestExecutor executor, String uri, E data, boolean doNotAutoRefresh) throws WxErrorException {
     E dataForLog = DataUtils.handleDataWithSecret(data);
 
     if (uri.contains("access_token=")) {
@@ -399,9 +399,11 @@ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestH
         } finally {
           lock.unlock();
         }
-        if (this.getWxMpConfigStorage().autoRefreshToken()) {
+        if (this.getWxMpConfigStorage().autoRefreshToken() && !doNotAutoRefresh) {
           log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
-          return this.execute(executor, uri, data);
+          //下一次不再自动重试
+          //当小程序误调用第三方平台专属接口时,第三方无法使用小程序的access token,如果可以继续自动获取token会导致无限循环重试,直到栈溢出
+          return this.executeInternal(executor, uri, data, true);
         }
       }
 
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
index 938eb5c03..6e29d0c64 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
@@ -1,12 +1,12 @@
 package me.chanjar.weixin.mp.api;
 
 import lombok.extern.slf4j.Slf4j;
-import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.error.WxRuntimeException;
 import me.chanjar.weixin.common.util.http.RequestExecutor;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
-import org.testng.annotations.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -23,7 +23,7 @@ public class WxMpBusyRetryTest {
 
       @Override
       public synchronized  T executeInternal(
-        RequestExecutor executor, String uri, E data)
+        RequestExecutor executor, String uri, E data, boolean doNotAutoRefresh)
         throws WxErrorException {
         log.info("Executed");
         throw new WxErrorException("something");
@@ -37,7 +37,7 @@ public class WxMpBusyRetryTest {
 
   @Test(dataProvider = "getService", expectedExceptions = RuntimeException.class)
   public void testRetry(WxMpService service) throws WxErrorException {
-    service.execute(null, (String)null, null);
+    service.execute(null, (String) null, null);
   }
 
   @Test(dataProvider = "getService")
@@ -48,7 +48,7 @@ public class WxMpBusyRetryTest {
       try {
         System.out.println("=====================");
         System.out.println(Thread.currentThread().getName() + ": testRetry");
-        service.execute(null, (String)null, null);
+        service.execute(null, (String) null, null);
       } catch (WxErrorException e) {
         throw new WxRuntimeException(e);
       } catch (RuntimeException e) {
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java
index 1bb892227..c4b57ff13 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java
@@ -2,27 +2,35 @@ package me.chanjar.weixin.mp.api.impl;
 
 import com.google.common.collect.Sets;
 import com.google.inject.Inject;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.bean.WxJsapiSignature;
 import me.chanjar.weixin.common.bean.WxNetCheckResult;
+import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.error.WxMpErrorMsgEnum;
+import me.chanjar.weixin.common.util.http.HttpType;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
 import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder;
+import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
+import java.io.IOException;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.*;
 
 /**
  * 
@@ -93,7 +101,7 @@ public class BaseWxMpServiceImplTest {
 
     final int threadNumber = 10;
     ExecutorService executorService = Executors.newFixedThreadPool(threadNumber);
-    for ( int i = 0; i < threadNumber; i++ ) {
+    for (int i = 0; i < threadNumber; i++) {
       executorService.submit(r);
     }
     executorService.shutdown();
@@ -190,7 +198,57 @@ public class BaseWxMpServiceImplTest {
   }
 
   @Test
-  public void testExecute() {
+  public void testExecute() throws WxErrorException, IOException {
+
+  }
+
+  @Test
+  public void testExecuteAutoRefreshToken() throws WxErrorException, IOException {
+    //测试access token获取时的重试机制
+    BaseWxMpServiceImpl service = new BaseWxMpServiceImpl() {
+      @Override
+      public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+        return "模拟一个过期的access token:" + System.currentTimeMillis();
+      }
+
+      @Override
+      public void initHttp() {
+
+      }
+
+      @Override
+      public Object getRequestHttpClient() {
+        return null;
+      }
+
+      @Override
+      public Object getRequestHttpProxy() {
+        return null;
+      }
+
+      @Override
+      public HttpType getRequestType() {
+        return null;
+      }
+    };
+    WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
+    config.setAppId("1");
+    service.setWxMpConfigStorage(config);
+    RequestExecutor re = mock(RequestExecutor.class);
+
+    AtomicInteger counter = new AtomicInteger();
+    Mockito.when(re.execute(Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
+      counter.incrementAndGet();
+      WxError error = WxError.builder().errorCode(WxMpErrorMsgEnum.CODE_40001.getCode()).errorMsg(WxMpErrorMsgEnum.CODE_40001.getMsg()).build();
+      throw new WxErrorException(error);
+    });
+    try {
+      Object execute = service.execute(re, "http://baidu.com", new HashMap<>());
+      Assert.assertTrue(false, "代码应该不会执行到这里");
+    } catch (WxErrorException e) {
+      Assert.assertEquals(WxMpErrorMsgEnum.CODE_40001.getCode(), e.getError().getErrorCode());
+      Assert.assertEquals(2, counter.get());
+    }
   }
 
   @Test