Skip to content

Commit e93380c

Browse files
authored
🎨 #3873 【企业微信】修复会话存档API使用错误secret导致权限异常的问题
1 parent 2a9cff0 commit e93380c

13 files changed

+627
-3
lines changed

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

Lines changed: 26 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/91782
65+
* </pre>
66+
*
67+
* @param forceRefresh 强制刷新
68+
* @return 会话存档专用的access token
69+
* @throws WxErrorException the wx error exception
70+
*/
71+
String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException;
72+
6073
/**
6174
* 获得jsapi_ticket,不强制刷新jsapi_ticket
6275
*
@@ -194,6 +207,19 @@ public interface WxCpService extends WxService {
194207
*/
195208
String postWithoutToken(String url, String postData) throws WxErrorException;
196209

210+
/**
211+
* <pre>
212+
* 使用会话存档access token发起post请求
213+
* 会话存档相关API需要使用会话存档专用的secret获取独立的access token
214+
* </pre>
215+
*
216+
* @param url 接口地址
217+
* @param postData 请求body字符串
218+
* @return the string
219+
* @throws WxErrorException the wx error exception
220+
*/
221+
String postForMsgAudit(String url, String postData) throws WxErrorException;
222+
197223
/**
198224
* <pre>
199225
* Service没有实现某个API的时候,可以用这个,

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,16 @@ public String postWithoutToken(String url, String postData) throws WxErrorExcept
302302
return this.executeNormal(SimplePostRequestExecutor.create(this), url, postData);
303303
}
304304

305+
@Override
306+
public String postForMsgAudit(String url, String postData) throws WxErrorException {
307+
// 获取会话存档专用的access token
308+
String msgAuditAccessToken = getMsgAuditAccessToken(false);
309+
// 拼接access_token参数
310+
String urlWithToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + msgAuditAccessToken;
311+
// 使用executeNormal方法,不自动添加token
312+
return this.executeNormal(SimplePostRequestExecutor.create(this), urlWithToken, postData);
313+
}
314+
305315
/**
306316
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求.
307317
*/

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ public List<String> getPermitUserList(Integer type) throws WxErrorException {
302302
if (type != null) {
303303
jsonObject.addProperty("type", type);
304304
}
305-
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
305+
String responseContent = this.cpService.postForMsgAudit(apiUrl, jsonObject.toString());
306306
return WxCpGsonBuilder.create().fromJson(GsonParser.parse(responseContent).getAsJsonArray("ids"),
307307
new TypeToken<List<String>>() {
308308
}.getType());
@@ -313,14 +313,14 @@ public WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorExceptio
313313
final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_GROUP_CHAT);
314314
JsonObject jsonObject = new JsonObject();
315315
jsonObject.addProperty("roomid", roomid);
316-
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
316+
String responseContent = this.cpService.postForMsgAudit(apiUrl, jsonObject.toString());
317317
return WxCpGroupChat.fromJson(responseContent);
318318
}
319319

320320
@Override
321321
public WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException {
322322
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CHECK_SINGLE_AGREE);
323-
String responseContent = this.cpService.post(apiUrl, checkAgreeRequest.toJson());
323+
String responseContent = this.cpService.postForMsgAudit(apiUrl, checkAgreeRequest.toJson());
324324
return WxCpAgreeInfo.fromJson(responseContent);
325325
}
326326

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.apache.http.impl.client.CloseableHttpClient;
1818

1919
import java.io.IOException;
20+
import java.util.concurrent.locks.Lock;
2021

2122
/**
2223
* The type Wx cp service apache http client.
@@ -74,6 +75,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7475
return this.configStorage.getAccessToken();
7576
}
7677

78+
@Override
79+
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
80+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
81+
return this.configStorage.getMsgAuditAccessToken();
82+
}
83+
84+
Lock lock = this.configStorage.getMsgAuditAccessTokenLock();
85+
lock.lock();
86+
try {
87+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
88+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
89+
return this.configStorage.getMsgAuditAccessToken();
90+
}
91+
// 使用会话存档secret获取access_token
92+
String msgAuditSecret = this.configStorage.getMsgAuditSecret();
93+
if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) {
94+
throw new WxErrorException("会话存档secret未配置");
95+
}
96+
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
97+
this.configStorage.getCorpId(), msgAuditSecret);
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.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
114+
} catch (IOException e) {
115+
throw new WxRuntimeException(e);
116+
}
117+
} finally {
118+
lock.unlock();
119+
}
120+
return this.configStorage.getMsgAuditAccessToken();
121+
}
122+
77123
@Override
78124
public void initHttp() {
79125
ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.apache.hc.core5.http.HttpHost;
1818

1919
import java.io.IOException;
20+
import java.util.concurrent.locks.Lock;
2021

2122
/**
2223
* The type Wx cp service apache http client.
@@ -75,6 +76,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7576
return this.configStorage.getAccessToken();
7677
}
7778

79+
@Override
80+
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
81+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
82+
return this.configStorage.getMsgAuditAccessToken();
83+
}
84+
85+
Lock lock = this.configStorage.getMsgAuditAccessTokenLock();
86+
lock.lock();
87+
try {
88+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
89+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
90+
return this.configStorage.getMsgAuditAccessToken();
91+
}
92+
// 使用会话存档secret获取access_token
93+
String msgAuditSecret = this.configStorage.getMsgAuditSecret();
94+
if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) {
95+
throw new WxErrorException("会话存档secret未配置");
96+
}
97+
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
98+
this.configStorage.getCorpId(), msgAuditSecret);
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.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
115+
} catch (IOException e) {
116+
throw new WxRuntimeException(e);
117+
}
118+
} finally {
119+
lock.unlock();
120+
}
121+
return this.configStorage.getMsgAuditAccessToken();
122+
}
123+
78124
@Override
79125
public void initHttp() {
80126
HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get();

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 getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
75+
final WxCpConfigStorage configStorage = getWxCpConfigStorage();
76+
if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
77+
return configStorage.getMsgAuditAccessToken();
78+
}
79+
Lock lock = configStorage.getMsgAuditAccessTokenLock();
80+
lock.lock();
81+
try {
82+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
83+
if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
84+
return configStorage.getMsgAuditAccessToken();
85+
}
86+
// 使用会话存档secret获取access_token
87+
String msgAuditSecret = configStorage.getMsgAuditSecret();
88+
if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) {
89+
throw new WxErrorException("会话存档secret未配置");
90+
}
91+
String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
92+
this.configStorage.getCorpId(), msgAuditSecret);
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.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
107+
} catch (IOException e) {
108+
throw new WxRuntimeException(e);
109+
}
110+
} finally {
111+
lock.unlock();
112+
}
113+
return configStorage.getMsgAuditAccessToken();
114+
}
115+
73116
@Override
74117
public String getAgentJsapiTicket(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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
1414
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
1515

16+
import java.util.concurrent.locks.Lock;
17+
1618
/**
1719
* The type Wx cp service jodd http.
1820
*
@@ -63,6 +65,45 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
6365
return this.configStorage.getAccessToken();
6466
}
6567

68+
@Override
69+
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
70+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
71+
return this.configStorage.getMsgAuditAccessToken();
72+
}
73+
74+
Lock lock = this.configStorage.getMsgAuditAccessTokenLock();
75+
lock.lock();
76+
try {
77+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
78+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
79+
return this.configStorage.getMsgAuditAccessToken();
80+
}
81+
// 使用会话存档secret获取access_token
82+
String msgAuditSecret = this.configStorage.getMsgAuditSecret();
83+
if (msgAuditSecret == null || msgAuditSecret.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(), msgAuditSecret));
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.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
101+
} finally {
102+
lock.unlock();
103+
}
104+
return this.configStorage.getMsgAuditAccessToken();
105+
}
106+
66107
@Override
67108
public void initHttp() {
68109
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) {

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import okhttp3.*;
1313

1414
import java.io.IOException;
15+
import java.util.concurrent.locks.Lock;
1516

1617
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN;
1718

@@ -74,6 +75,52 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
7475
return this.configStorage.getAccessToken();
7576
}
7677

78+
@Override
79+
public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException {
80+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
81+
return this.configStorage.getMsgAuditAccessToken();
82+
}
83+
84+
Lock lock = this.configStorage.getMsgAuditAccessTokenLock();
85+
lock.lock();
86+
try {
87+
// 拿到锁之后,再次判断一下最新的token是否过期,避免重刷
88+
if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) {
89+
return this.configStorage.getMsgAuditAccessToken();
90+
}
91+
// 使用会话存档secret获取access_token
92+
String msgAuditSecret = this.configStorage.getMsgAuditSecret();
93+
if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) {
94+
throw new WxErrorException("会话存档secret未配置");
95+
}
96+
//得到httpClient
97+
OkHttpClient client = getRequestHttpClient();
98+
//请求的request
99+
Request request = new Request.Builder()
100+
.url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(),
101+
msgAuditSecret))
102+
.get()
103+
.build();
104+
String resultContent = null;
105+
try (Response response = client.newCall(request).execute()) {
106+
resultContent = response.body().string();
107+
} catch (IOException e) {
108+
log.error(e.getMessage(), e);
109+
}
110+
111+
WxError error = WxError.fromJson(resultContent, WxType.CP);
112+
if (error.getErrorCode() != 0) {
113+
throw new WxErrorException(error);
114+
}
115+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
116+
this.configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(),
117+
accessToken.getExpiresIn());
118+
} finally {
119+
lock.unlock();
120+
}
121+
return this.configStorage.getMsgAuditAccessToken();
122+
}
123+
77124
@Override
78125
public void initHttp() {
79126
log.debug("WxCpServiceOkHttpImpl initHttp");

0 commit comments

Comments
 (0)