🎨 #4047 【企业微信】通讯录同步相关接口补齐“独立 secret + 独立 access token”的调用通道
This commit is contained in:
@@ -57,6 +57,19 @@ public interface WxCpService extends WxService {
|
||||
*/
|
||||
String getAccessToken(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获取通讯录同步access_token,本方法线程安全
|
||||
* 通讯录同步相关接口仅支持通过"通讯录同步secret"调用,需要使用独立的access_token
|
||||
* 详情请见: https://developer.work.weixin.qq.com/document/path/91579
|
||||
* </pre>
|
||||
*
|
||||
* @param forceRefresh 强制刷新
|
||||
* @return 通讯录同步专用的access token
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String getContactAccessToken(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获取会话存档access_token,本方法线程安全
|
||||
@@ -220,6 +233,32 @@ public interface WxCpService extends WxService {
|
||||
*/
|
||||
String postForMsgAudit(String url, String postData) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 使用通讯录同步access token发起get请求
|
||||
* 通讯录同步相关API需要使用通讯录同步专用的secret获取独立的access token
|
||||
* </pre>
|
||||
*
|
||||
* @param url 接口地址
|
||||
* @param queryParam 请求参数
|
||||
* @return the string
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String getForContact(String url, String queryParam) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 使用通讯录同步access token发起post请求
|
||||
* 通讯录同步相关API需要使用通讯录同步专用的secret获取独立的access token
|
||||
* </pre>
|
||||
*
|
||||
* @param url 接口地址
|
||||
* @param postData 请求body字符串
|
||||
* @return the string
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String postForContact(String url, String postData) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Service没有实现某个API的时候,可以用这个,
|
||||
|
||||
@@ -313,6 +313,29 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
|
||||
return this.executeNormal(SimplePostRequestExecutor.create(this), urlWithToken, postData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForContact(String url, String queryParam) throws WxErrorException {
|
||||
// 获取通讯录同步专用的access token
|
||||
String contactAccessToken = getContactAccessToken(false);
|
||||
// 拼接access_token参数
|
||||
String urlWithToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + contactAccessToken;
|
||||
if (queryParam != null && !queryParam.isEmpty()) {
|
||||
urlWithToken = urlWithToken + "&" + queryParam;
|
||||
}
|
||||
// 使用executeNormal方法,不自动添加token
|
||||
return this.executeNormal(SimpleGetRequestExecutor.create(this), urlWithToken, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postForContact(String url, String postData) throws WxErrorException {
|
||||
// 获取通讯录同步专用的access token
|
||||
String contactAccessToken = getContactAccessToken(false);
|
||||
// 拼接access_token参数
|
||||
String urlWithToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + contactAccessToken;
|
||||
// 使用executeNormal方法,不自动添加token
|
||||
return this.executeNormal(SimplePostRequestExecutor.create(this), urlWithToken, postData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求.
|
||||
*/
|
||||
|
||||
@@ -75,6 +75,51 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab
|
||||
return this.configStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
Lock lock = this.configStorage.getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = this.configStorage.getContactSecret();
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
|
||||
this.configStorage.getCorpId(), contactSecret);
|
||||
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (this.httpProxy != null) {
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setProxy(this.httpProxy).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE);
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
|
||||
} catch (IOException e) {
|
||||
throw new WxRuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
|
||||
|
||||
@@ -76,6 +76,51 @@ public class WxCpServiceHttpComponentsImpl extends BaseWxCpServiceImpl<Closeable
|
||||
return this.configStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
Lock lock = this.configStorage.getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = this.configStorage.getContactSecret();
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
|
||||
this.configStorage.getCorpId(), contactSecret);
|
||||
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (this.httpProxy != null) {
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setProxy(this.httpProxy).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
String resultContent = getRequestHttpClient().execute(httpGet, BasicResponseHandler.INSTANCE);
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
|
||||
} catch (IOException e) {
|
||||
throw new WxRuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
|
||||
|
||||
@@ -70,6 +70,49 @@ public class WxCpServiceImpl extends WxCpServiceApacheHttpClientImpl {
|
||||
return configStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
final WxCpConfigStorage configStorage = getWxCpConfigStorage();
|
||||
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return configStorage.getContactAccessToken();
|
||||
}
|
||||
Lock lock = configStorage.getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
|
||||
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return configStorage.getContactAccessToken();
|
||||
}
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = configStorage.getContactSecret();
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
|
||||
this.configStorage.getCorpId(), contactSecret);
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (getRequestHttpProxy() != null) {
|
||||
RequestConfig config = RequestConfig.custom().setProxy(getRequestHttpProxy()).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE);
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
|
||||
} catch (IOException e) {
|
||||
throw new WxRuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
final WxCpConfigStorage configStorage = getWxCpConfigStorage();
|
||||
|
||||
@@ -65,6 +65,45 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP
|
||||
return this.configStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
Lock lock = this.configStorage.getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = this.configStorage.getContactSecret();
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
|
||||
this.configStorage.getCorpId(), contactSecret));
|
||||
if (this.httpProxy != null) {
|
||||
httpClient.useProxy(this.httpProxy);
|
||||
}
|
||||
request.withConnectionProvider(httpClient);
|
||||
HttpResponse response = request.send();
|
||||
|
||||
String resultContent = response.bodyText();
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
|
||||
|
||||
@@ -56,12 +56,15 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
|
||||
this.configStorage.getCorpSecret()))
|
||||
.get()
|
||||
.build();
|
||||
String resultContent = null;
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
String resultContent;
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (response.body() == null) {
|
||||
throw new WxErrorException("请求access token失败:响应内容为空");
|
||||
}
|
||||
resultContent = response.body().string();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new WxErrorException(e);
|
||||
}
|
||||
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
@@ -75,6 +78,55 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
|
||||
return this.configStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
Lock lock = this.configStorage.getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
|
||||
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = this.configStorage.getContactSecret();
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
//得到httpClient
|
||||
OkHttpClient client = getRequestHttpClient();
|
||||
//请求的request
|
||||
Request request = new Request.Builder()
|
||||
.url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(),
|
||||
contactSecret))
|
||||
.get()
|
||||
.build();
|
||||
String resultContent;
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (response.body() == null) {
|
||||
throw new WxErrorException("请求通讯录同步access token失败:响应内容为空");
|
||||
}
|
||||
resultContent = response.body().string();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new WxErrorException(e);
|
||||
}
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(),
|
||||
accessToken.getExpiresIn());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return this.configStorage.getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
|
||||
@@ -101,11 +153,15 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
|
||||
msgAuditSecret))
|
||||
.get()
|
||||
.build();
|
||||
String resultContent = null;
|
||||
String resultContent;
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (response.body() == null) {
|
||||
throw new WxErrorException("请求会话存档access token失败:响应内容为空");
|
||||
}
|
||||
resultContent = response.body().string();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new WxErrorException(e);
|
||||
}
|
||||
|
||||
WxError error = WxError.fromJson(resultContent, WxType.CP);
|
||||
|
||||
@@ -258,6 +258,47 @@ public interface WxCpConfigStorage {
|
||||
*/
|
||||
String getWebhookKey();
|
||||
|
||||
/**
|
||||
* 获取通讯录同步的secret
|
||||
*
|
||||
* @return contact secret
|
||||
*/
|
||||
String getContactSecret();
|
||||
|
||||
/**
|
||||
* 获取通讯录同步的access token
|
||||
*
|
||||
* @return contact access token
|
||||
*/
|
||||
String getContactAccessToken();
|
||||
|
||||
/**
|
||||
* 获取通讯录同步access token的锁
|
||||
*
|
||||
* @return contact access token lock
|
||||
*/
|
||||
Lock getContactAccessTokenLock();
|
||||
|
||||
/**
|
||||
* 检查通讯录同步access token是否已过期
|
||||
*
|
||||
* @return true: 已过期, false: 未过期
|
||||
*/
|
||||
boolean isContactAccessTokenExpired();
|
||||
|
||||
/**
|
||||
* 强制将通讯录同步access token过期掉
|
||||
*/
|
||||
void expireContactAccessToken();
|
||||
|
||||
/**
|
||||
* 更新通讯录同步access token
|
||||
*
|
||||
* @param accessToken 通讯录同步access token
|
||||
* @param expiresInSeconds 过期时间(秒)
|
||||
*/
|
||||
void updateContactAccessToken(String accessToken, int expiresInSeconds);
|
||||
|
||||
/**
|
||||
* 获取会话存档的secret
|
||||
*
|
||||
|
||||
@@ -44,6 +44,16 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
|
||||
private volatile String token;
|
||||
private volatile String aesKey;
|
||||
private volatile long expiresTime;
|
||||
/**
|
||||
* 通讯录同步secret及其access token
|
||||
*/
|
||||
private volatile String contactSecret;
|
||||
private volatile String contactAccessToken;
|
||||
private volatile long contactAccessTokenExpiresTime;
|
||||
/**
|
||||
* 通讯录同步access token锁
|
||||
*/
|
||||
protected transient Lock contactAccessTokenLock = new ReentrantLock();
|
||||
/**
|
||||
* 会话存档私钥以及sdk路径
|
||||
*/
|
||||
@@ -465,6 +475,49 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactSecret() {
|
||||
return this.contactSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置通讯录同步secret.
|
||||
*
|
||||
* @param contactSecret 通讯录同步secret
|
||||
* @return this
|
||||
*/
|
||||
public WxCpDefaultConfigImpl setContactSecret(String contactSecret) {
|
||||
this.contactSecret = contactSecret;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken() {
|
||||
return this.contactAccessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getContactAccessTokenLock() {
|
||||
return this.contactAccessTokenLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContactAccessTokenExpired() {
|
||||
return System.currentTimeMillis() > this.contactAccessTokenExpiresTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireContactAccessToken() {
|
||||
this.contactAccessTokenExpiresTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void updateContactAccessToken(String accessToken, int expiresInSeconds) {
|
||||
this.contactAccessToken = accessToken;
|
||||
// 预留200秒的时间
|
||||
this.contactAccessTokenExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditSecret() {
|
||||
return this.msgAuditSecret;
|
||||
|
||||
@@ -492,6 +492,36 @@ public class WxCpRedisConfigImpl implements WxCpConfigStorage {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactSecret() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getContactAccessTokenLock() {
|
||||
return this.msgAuditAccessTokenLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContactAccessTokenExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireContactAccessToken() {
|
||||
// 不支持
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContactAccessToken(String accessToken, int expiresInSeconds) {
|
||||
// 不支持
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken() {
|
||||
return null;
|
||||
|
||||
@@ -106,6 +106,11 @@ public class BaseWxCpServiceImplTest {
|
||||
return "mock_msg_audit_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "mock_contact_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.http.HttpClientType;
|
||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
|
||||
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* 测试 getContactAccessToken 方法在各个实现类中的正确性
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Test
|
||||
public class WxCpServiceGetContactAccessTokenTest {
|
||||
|
||||
private WxCpDefaultConfigImpl config;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
config = new WxCpDefaultConfigImpl();
|
||||
config.setCorpId("testCorpId");
|
||||
config.setCorpSecret("testCorpSecret");
|
||||
config.setContactSecret("testContactSecret");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试通讯录同步access token的缓存机制
|
||||
* 验证当token未过期时,直接从配置中返回缓存的token
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_Cache() throws WxErrorException {
|
||||
// 预先设置一个有效的token
|
||||
config.updateContactAccessToken("cached_token", 7200);
|
||||
|
||||
BaseWxCpServiceImpl service = createTestService(config);
|
||||
|
||||
// 不强制刷新时应该返回缓存的token
|
||||
String token = service.getContactAccessToken(false);
|
||||
assertThat(token).isEqualTo("cached_token");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试强制刷新通讯录同步access token
|
||||
* 验证forceRefresh=true时会重新获取token
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_ForceRefresh() throws WxErrorException {
|
||||
// 预先设置一个有效的token
|
||||
config.updateContactAccessToken("old_token", 7200);
|
||||
|
||||
BaseWxCpServiceImpl service = createTestServiceWithMockToken(config, "new_token");
|
||||
|
||||
// 强制刷新应该获取新token
|
||||
String token = service.getContactAccessToken(true);
|
||||
assertThat(token).isEqualTo("new_token");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试token过期时自动刷新
|
||||
* 验证当token已过期时,会自动重新获取
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_Expired() throws WxErrorException {
|
||||
// 设置一个已过期的token(过期时间为负数,确保立即过期)
|
||||
config.updateContactAccessToken("expired_token", -1);
|
||||
|
||||
BaseWxCpServiceImpl service = createTestServiceWithMockToken(config, "refreshed_token");
|
||||
|
||||
// 过期的token应该被自动刷新
|
||||
String token = service.getContactAccessToken(false);
|
||||
assertThat(token).isEqualTo("refreshed_token");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取锁机制
|
||||
* 验证配置中的锁可以正常获取和使用
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_Lock() {
|
||||
// 验证配置提供的锁不为null
|
||||
assertThat(config.getContactAccessTokenLock()).isNotNull();
|
||||
|
||||
// 验证锁可以正常使用
|
||||
config.getContactAccessTokenLock().lock();
|
||||
try {
|
||||
assertThat(config.getContactAccessToken()).isNull();
|
||||
} finally {
|
||||
config.getContactAccessTokenLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查token是否需要刷新的公共逻辑
|
||||
*/
|
||||
private boolean shouldRefreshToken(WxCpConfigStorage storage, boolean forceRefresh) {
|
||||
return storage.isContactAccessTokenExpired() || forceRefresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证通讯录同步secret是否已配置的公共逻辑
|
||||
*/
|
||||
private void validateContactSecret(String contactSecret) throws WxErrorException {
|
||||
if (contactSecret == null || contactSecret.trim().isEmpty()) {
|
||||
throw new WxErrorException("通讯录同步secret未配置");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个用于测试的BaseWxCpServiceImpl实现,
|
||||
* 用于测试缓存和过期逻辑
|
||||
*/
|
||||
private BaseWxCpServiceImpl createTestService(WxCpConfigStorage config) {
|
||||
return new BaseWxCpServiceImpl() {
|
||||
@Override
|
||||
public Object getRequestHttpClient() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRequestHttpProxy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClientType getRequestType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "test_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
// 检查是否需要刷新
|
||||
if (!shouldRefreshToken(getWxCpConfigStorage(), forceRefresh)) {
|
||||
return getWxCpConfigStorage().getContactAccessToken();
|
||||
}
|
||||
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = getWxCpConfigStorage().getContactSecret();
|
||||
validateContactSecret(contactSecret);
|
||||
|
||||
// 返回缓存的token(用于测试缓存机制)
|
||||
return getWxCpConfigStorage().getContactAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "test_msg_audit_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpConfigStorage getWxCpConfigStorage() {
|
||||
return config;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个用于测试的BaseWxCpServiceImpl实现,
|
||||
* 模拟返回指定的token(用于测试刷新逻辑)
|
||||
*/
|
||||
private BaseWxCpServiceImpl createTestServiceWithMockToken(WxCpConfigStorage config, String mockToken) {
|
||||
return new BaseWxCpServiceImpl() {
|
||||
@Override
|
||||
public Object getRequestHttpClient() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRequestHttpProxy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClientType getRequestType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "test_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
// 使用锁机制
|
||||
Lock lock = getWxCpConfigStorage().getContactAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
// 检查是否需要刷新
|
||||
if (!shouldRefreshToken(getWxCpConfigStorage(), forceRefresh)) {
|
||||
return getWxCpConfigStorage().getContactAccessToken();
|
||||
}
|
||||
|
||||
// 使用通讯录同步secret获取access_token
|
||||
String contactSecret = getWxCpConfigStorage().getContactSecret();
|
||||
validateContactSecret(contactSecret);
|
||||
|
||||
// 模拟获取新token并更新配置
|
||||
getWxCpConfigStorage().updateContactAccessToken(mockToken, 7200);
|
||||
return mockToken;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "test_msg_audit_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpConfigStorage getWxCpConfigStorage() {
|
||||
return config;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试当 ContactSecret 未配置时应该抛出异常
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_WithoutSecret() {
|
||||
config.setContactSecret(null);
|
||||
BaseWxCpServiceImpl service = createTestService(config);
|
||||
|
||||
// 验证当 secret 为 null 时抛出异常
|
||||
assertThatThrownBy(() -> service.getContactAccessToken(true))
|
||||
.isInstanceOf(WxErrorException.class)
|
||||
.hasMessageContaining("通讯录同步secret未配置");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试当 ContactSecret 为空字符串时应该抛出异常
|
||||
*/
|
||||
@Test
|
||||
public void testGetContactAccessToken_WithEmptySecret() {
|
||||
config.setContactSecret(" ");
|
||||
BaseWxCpServiceImpl service = createTestService(config);
|
||||
|
||||
// 验证当 secret 为空字符串时抛出异常
|
||||
assertThatThrownBy(() -> service.getContactAccessToken(true))
|
||||
.isInstanceOf(WxErrorException.class)
|
||||
.hasMessageContaining("通讯录同步secret未配置");
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,11 @@ public class WxCpServiceGetMsgAuditAccessTokenTest {
|
||||
return getWxCpConfigStorage().getMsgAuditAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "mock_contact_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
}
|
||||
@@ -213,6 +218,11 @@ public class WxCpServiceGetMsgAuditAccessTokenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
return "mock_contact_access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user