🎨 #2115 优化公众号、小程序、企业微信的access token自动刷新逻辑,避免循环递归调用

This commit is contained in:
hywr
2021-05-20 14:23:26 +08:00
committed by GitHub
parent dfb02eac38
commit 62a3931aee
8 changed files with 200 additions and 28 deletions

View File

@ -241,7 +241,7 @@ public abstract class BaseWxCpServiceImpl<H, P> 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<H, P> implements WxCpService, RequestH
throw new WxRuntimeException("微信服务端异常,超出重试次数");
}
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
protected <T, E> T executeInternal(RequestExecutor<T, E> 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<H, P> 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);
}
}

View File

@ -22,7 +22,7 @@ public class WxCpBusyRetryTest {
@Override
public synchronized <T, E> T executeInternal(
RequestExecutor<T, E> executor, String uri, E data)
RequestExecutor<T, E> executor, String uri, E data, boolean doNotAutoRefresh)
throws WxErrorException {
log.info("Executed");
throw new WxErrorException("something");

View File

@ -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;
/**
* <pre>
@ -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<Object, Object> 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());
}
}
}