Skip to content

Commit f2f5957

Browse files
authored
🎨 #4047 【企业微信】通讯录同步相关接口补齐“独立 secret + 独立 access token”的调用通道
1 parent 25a3328 commit f2f5957

13 files changed

Lines changed: 697 additions & 4 deletions

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ public interface WxCpService extends WxService {
5757
*/
5858
String getAccessToken(boolean forceRefresh) throws WxErrorException;
5959

60+
/**
61+
* <pre>
62+
* 获取通讯录同步access_token,本方法线程安全
63+
* 通讯录同步相关接口仅支持通过"通讯录同步secret"调用,需要使用独立的access_token
64+
* 详情请见: https://developer.work.weixin.qq.com/document/path/91579
65+
* </pre>
66+
*
67+
* @param forceRefresh 强制刷新
68+
* @return 通讯录同步专用的access token
69+
* @throws WxErrorException the wx error exception
70+
*/
71+
String getContactAccessToken(boolean forceRefresh) throws WxErrorException;
72+
6073
/**
6174
* <pre>
6275
* 获取会话存档access_token,本方法线程安全
@@ -220,6 +233,32 @@ public interface WxCpService extends WxService {
220233
*/
221234
String postForMsgAudit(String url, String postData) throws WxErrorException;
222235

236+
/**
237+
* <pre>
238+
* 使用通讯录同步access token发起get请求
239+
* 通讯录同步相关API需要使用通讯录同步专用的secret获取独立的access token
240+
* </pre>
241+
*
242+
* @param url 接口地址
243+
* @param queryParam 请求参数
244+
* @return the string
245+
* @throws WxErrorException the wx error exception
246+
*/
247+
String getForContact(String url, String queryParam) throws WxErrorException;
248+
249+
/**
250+
* <pre>
251+
* 使用通讯录同步access token发起post请求
252+
* 通讯录同步相关API需要使用通讯录同步专用的secret获取独立的access token
253+
* </pre>
254+
*
255+
* @param url 接口地址
256+
* @param postData 请求body字符串
257+
* @return the string
258+
* @throws WxErrorException the wx error exception
259+
*/
260+
String postForContact(String url, String postData) throws WxErrorException;
261+
223262
/**
224263
* <pre>
225264
* Service没有实现某个API的时候,可以用这个,

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,29 @@ public String postForMsgAudit(String url, String postData) throws WxErrorExcepti
313313
return this.executeNormal(SimplePostRequestExecutor.create(this), urlWithToken, postData);
314314
}
315315

316+
@Override
317+
public String getForContact(String url, String queryParam) throws WxErrorException {
318+
// 获取通讯录同步专用的access token
319+
String contactAccessToken = getContactAccessToken(false);
320+
// 拼接access_token参数
321+
String urlWithToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + contactAccessToken;
322+
if (queryParam != null && !queryParam.isEmpty()) {
323+
urlWithToken = urlWithToken + "&" + queryParam;
324+
}
325+
// 使用executeNormal方法,不自动添加token
326+
return this.executeNormal(SimpleGetRequestExecutor.create(this), urlWithToken, null);
327+
}
328+
329+
@Override
330+
public String postForContact(String url, String postData) throws WxErrorException {
331+
// 获取通讯录同步专用的access token
332+
String contactAccessToken = getContactAccessToken(false);
333+
// 拼接access_token参数
334+
String urlWithToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + contactAccessToken;
335+
// 使用executeNormal方法,不自动添加token
336+
return this.executeNormal(SimplePostRequestExecutor.create(this), urlWithToken, postData);
337+
}
338+
316339
/**
317340
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求.
318341
*/

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7575
return this.configStorage.getAccessToken();
7676
}
7777

78+
@Override
79+
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
80+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
81+
return this.configStorage.getContactAccessToken();
82+
}
83+
84+
Lock lock = this.configStorage.getContactAccessTokenLock();
85+
lock.lock();
86+
try {
87+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
88+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
89+
return this.configStorage.getContactAccessToken();
90+
}
91+
// 使用通讯录同步secret获取access_token
92+
String contactSecret = this.configStorage.getContactSecret();
93+
if (contactSecret == null || contactSecret.trim().isEmpty()) {
94+
throw new WxErrorException("通讯录同步secret未配置");
95+
}
96+
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
97+
this.configStorage.getCorpId(), contactSecret);
98+
99+
try {
100+
HttpGet httpGet = new HttpGet(url);
101+
if (this.httpProxy != null) {
102+
RequestConfig config = RequestConfig.custom()
103+
.setProxy(this.httpProxy).build();
104+
httpGet.setConfig(config);
105+
}
106+
String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE);
107+
WxError error = WxError.fromJson(resultContent, WxType.CP);
108+
if (error.getErrorCode() != 0) {
109+
throw new WxErrorException(error);
110+
}
111+
112+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
113+
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
114+
} catch (IOException e) {
115+
throw new WxRuntimeException(e);
116+
}
117+
} finally {
118+
lock.unlock();
119+
}
120+
return this.configStorage.getContactAccessToken();
121+
}
122+
78123
@Override
79124
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
80125
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7676
return this.configStorage.getAccessToken();
7777
}
7878

79+
@Override
80+
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
81+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
82+
return this.configStorage.getContactAccessToken();
83+
}
84+
85+
Lock lock = this.configStorage.getContactAccessTokenLock();
86+
lock.lock();
87+
try {
88+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
89+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
90+
return this.configStorage.getContactAccessToken();
91+
}
92+
// 使用通讯录同步secret获取access_token
93+
String contactSecret = this.configStorage.getContactSecret();
94+
if (contactSecret == null || contactSecret.trim().isEmpty()) {
95+
throw new WxErrorException("通讯录同步secret未配置");
96+
}
97+
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
98+
this.configStorage.getCorpId(), contactSecret);
99+
100+
try {
101+
HttpGet httpGet = new HttpGet(url);
102+
if (this.httpProxy != null) {
103+
RequestConfig config = RequestConfig.custom()
104+
.setProxy(this.httpProxy).build();
105+
httpGet.setConfig(config);
106+
}
107+
String resultContent = getRequestHttpClient().execute(httpGet, BasicResponseHandler.INSTANCE);
108+
WxError error = WxError.fromJson(resultContent, WxType.CP);
109+
if (error.getErrorCode() != 0) {
110+
throw new WxErrorException(error);
111+
}
112+
113+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
114+
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
115+
} catch (IOException e) {
116+
throw new WxRuntimeException(e);
117+
}
118+
} finally {
119+
lock.unlock();
120+
}
121+
return this.configStorage.getContactAccessToken();
122+
}
123+
79124
@Override
80125
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
81126
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,49 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7070
return configStorage.getAccessToken();
7171
}
7272

73+
@Override
74+
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
75+
final WxCpConfigStorage configStorage = getWxCpConfigStorage();
76+
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
77+
return configStorage.getContactAccessToken();
78+
}
79+
Lock lock = configStorage.getContactAccessTokenLock();
80+
lock.lock();
81+
try {
82+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
83+
if (!configStorage.isContactAccessTokenExpired() && !forceRefresh) {
84+
return configStorage.getContactAccessToken();
85+
}
86+
// 使用通讯录同步secret获取access_token
87+
String contactSecret = configStorage.getContactSecret();
88+
if (contactSecret == null || contactSecret.trim().isEmpty()) {
89+
throw new WxErrorException("通讯录同步secret未配置");
90+
}
91+
String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
92+
this.configStorage.getCorpId(), contactSecret);
93+
try {
94+
HttpGet httpGet = new HttpGet(url);
95+
if (getRequestHttpProxy() != null) {
96+
RequestConfig config = RequestConfig.custom().setProxy(getRequestHttpProxy()).build();
97+
httpGet.setConfig(config);
98+
}
99+
String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE);
100+
WxError error = WxError.fromJson(resultContent, WxType.CP);
101+
if (error.getErrorCode() != 0) {
102+
throw new WxErrorException(error);
103+
}
104+
105+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
106+
configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
107+
} catch (IOException e) {
108+
throw new WxRuntimeException(e);
109+
}
110+
} finally {
111+
lock.unlock();
112+
}
113+
return configStorage.getContactAccessToken();
114+
}
115+
73116
@Override
74117
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
75118
final WxCpConfigStorage configStorage = getWxCpConfigStorage();

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,45 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
6565
return this.configStorage.getAccessToken();
6666
}
6767

68+
@Override
69+
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
70+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
71+
return this.configStorage.getContactAccessToken();
72+
}
73+
74+
Lock lock = this.configStorage.getContactAccessTokenLock();
75+
lock.lock();
76+
try {
77+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
78+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
79+
return this.configStorage.getContactAccessToken();
80+
}
81+
// 使用通讯录同步secret获取access_token
82+
String contactSecret = this.configStorage.getContactSecret();
83+
if (contactSecret == null || contactSecret.trim().isEmpty()) {
84+
throw new WxErrorException("通讯录同步secret未配置");
85+
}
86+
HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
87+
this.configStorage.getCorpId(), contactSecret));
88+
if (this.httpProxy != null) {
89+
httpClient.useProxy(this.httpProxy);
90+
}
91+
request.withConnectionProvider(httpClient);
92+
HttpResponse response = request.send();
93+
94+
String resultContent = response.bodyText();
95+
WxError error = WxError.fromJson(resultContent, WxType.CP);
96+
if (error.getErrorCode() != 0) {
97+
throw new WxErrorException(error);
98+
}
99+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
100+
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
101+
} finally {
102+
lock.unlock();
103+
}
104+
return this.configStorage.getContactAccessToken();
105+
}
106+
68107
@Override
69108
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
70109
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,15 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
5656
this.configStorage.getCorpSecret()))
5757
.get()
5858
.build();
59-
String resultContent = null;
60-
try {
61-
Response response = client.newCall(request).execute();
59+
String resultContent;
60+
try (Response response = client.newCall(request).execute()) {
61+
if (response.body() == null) {
62+
throw new WxErrorException("请求access token失败:响应内容为空");
63+
}
6264
resultContent = response.body().string();
6365
} catch (IOException e) {
6466
log.error(e.getMessage(), e);
67+
throw new WxErrorException(e);
6568
}
6669

6770
WxError error = WxError.fromJson(resultContent, WxType.CP);
@@ -75,6 +78,55 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7578
return this.configStorage.getAccessToken();
7679
}
7780

81+
@Override
82+
public String getContactAccessToken(boolean forceRefresh) throws WxErrorException {
83+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
84+
return this.configStorage.getContactAccessToken();
85+
}
86+
87+
Lock lock = this.configStorage.getContactAccessTokenLock();
88+
lock.lock();
89+
try {
90+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
91+
if (!this.configStorage.isContactAccessTokenExpired() && !forceRefresh) {
92+
return this.configStorage.getContactAccessToken();
93+
}
94+
// 使用通讯录同步secret获取access_token
95+
String contactSecret = this.configStorage.getContactSecret();
96+
if (contactSecret == null || contactSecret.trim().isEmpty()) {
97+
throw new WxErrorException("通讯录同步secret未配置");
98+
}
99+
//得到httpClient
100+
OkHttpClient client = getRequestHttpClient();
101+
//请求的request
102+
Request request = new Request.Builder()
103+
.url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(),
104+
contactSecret))
105+
.get()
106+
.build();
107+
String resultContent;
108+
try (Response response = client.newCall(request).execute()) {
109+
if (response.body() == null) {
110+
throw new WxErrorException("请求通讯录同步access token失败:响应内容为空");
111+
}
112+
resultContent = response.body().string();
113+
} catch (IOException e) {
114+
log.error(e.getMessage(), e);
115+
throw new WxErrorException(e);
116+
}
117+
WxError error = WxError.fromJson(resultContent, WxType.CP);
118+
if (error.getErrorCode() != 0) {
119+
throw new WxErrorException(error);
120+
}
121+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
122+
this.configStorage.updateContactAccessToken(accessToken.getAccessToken(),
123+
accessToken.getExpiresIn());
124+
} finally {
125+
lock.unlock();
126+
}
127+
return this.configStorage.getContactAccessToken();
128+
}
129+
78130
@Override
79131
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
80132
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
@@ -101,11 +153,15 @@ public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorExcepti
101153
msgAuditSecret))
102154
.get()
103155
.build();
104-
String resultContent = null;
156+
String resultContent;
105157
try (Response response = client.newCall(request).execute()) {
158+
if (response.body() == null) {
159+
throw new WxErrorException("请求会话存档access token失败:响应内容为空");
160+
}
106161
resultContent = response.body().string();
107162
} catch (IOException e) {
108163
log.error(e.getMessage(), e);
164+
throw new WxErrorException(e);
109165
}
110166

111167
WxError error = WxError.fromJson(resultContent, WxType.CP);

0 commit comments

Comments
 (0)