8000 :art: #2000 【企业微信】第三方应用增加基于分布式并发锁获取各种token和ticket的版本 · sishuiyunyan/WxJava@9d3c11f · GitHub
[go: up one dir, main page]

Skip to content

Commit 9d3c11f

Browse files
authored
🎨 binarywang#2000 【企业微信】第三方应用增加基于分布式并发锁获取各种token和ticket的版本
1 parent 3bb918d commit 9d3c11f

File tree

9 files changed

+664
-4
lines changed

9 files changed

+664
-4
lines changed

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import me.chanjar.weixin.common.bean.WxAccessToken;
44
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
5+
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
56

67
import java.io.File;
8+
import java.util.concurrent.locks.Lock;
79

810
/**
911
* 微信客户端(第三方应用)配置存储
@@ -30,6 +32,11 @@ public interface WxCpTpConfigStorage {
3032
* 第三方应用的suite access token相关
3133
*/
3234
String getSuiteAccessToken();
35+
/**
36+
* 获取suite_access_token和剩余过期时间
37+
* @return suite access token and the remaining expiration time
38+
*/
39+
WxAccessToken getSuiteAccessTokenEntity();
3340
boolean isSuiteAccessTokenExpired();
3441
//强制将suite access token过期掉.
3542
void expireSuiteAccessToken();
@@ -71,27 +78,34 @@ public interface WxCpTpConfigStorage {
7178
* 授权企业的access token相关
7279
*/
7380
String getAccessToken(String authCorpId);
81+
WxAccessToken getAccessTokenEntity(String authCorpId);
7482
boolean isAccessTokenExpired(String authCorpId);
83+
void expireAccessToken(String authCorpId);
7584
void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds);
7685

7786
/**
7887
* 授权企业的js api ticket相关
7988
*/
8089
String getAuthCorpJsApiTicket(String authCorpId);
8190
boolean isAuthCorpJsApiTicketExpired(String authCorpId);
91+
void expireAuthCorpJsApiTicket(String authCorpId);
8292
void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);
8393

8494
/**
8595
* 授权企业的第三方应用js api ticket相关
8696
*/
8797
String getAuthSuiteJsApiTicket(String authCorpId);
8898
boolean isAuthSuiteJsApiTicketExpired(String authCorpId);
99+
void expireAuthSuiteJsApiTicket(String authCorpId);
89100
void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);;
90101

91102
boolean isProviderTokenExpired();
92103
void updateProviderToken(String providerToken, int expiredInSeconds);
93104

94105
String getProviderToken();
106+
WxCpProviderToken getProviderTokenEntity();
107+
// 强制过期
108+
void expireProviderToken();
95109

96110
/**
97111
* 网络代理相关
@@ -108,4 +122,9 @@ public interface WxCpTpConfigStorage {
108122
@Deprecated
109123
File getTmpDirFile();
110124

125+
Lock getProviderAccessTokenLock();
126+
Lock getSuiteAccessTokenLock();
127+
Lock getAccessTokenLock(String authCorpId);
128+
Lock getAuthCorpJsapiTicketLock(String authCorpId);
129+
Lock getSuiteJsapiTicketLock(String authCorpId);
111130
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
import me.chanjar.weixin.common.bean.WxAccessToken;
44
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
5+
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
56
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
67
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
8+
import org.apache.commons.lang3.StringUtils;
79

810
import java.io.File;
911
import java.io.Serializable;
1012
import java.util.HashMap;
1113
import java.util.Map;
14+
import java.util.concurrent.ConcurrentHashMap;
15+
import java.util.concurrent.locks.Lock;
16+
import java.util.concurrent.locks.ReentrantLock;
1217

1318
/**
1419
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化.
@@ -59,6 +64,12 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
5964

6065
private volatile String baseApiUrl;
6166

67+
// locker
68+
private final transient Map<String, Lock> providerAccessTokenLocker = new ConcurrentHashMap<>();
69+
private final transient Map<String, Lock> suiteAccessTokenLocker = new ConcurrentHashMap<>();
70+
private final transient Map<String, Lock> accessTokenLocker = new ConcurrentHashMap<>();
71+
private final transient Map<String, Lock> authCorpJsapiTicketLocker = new ConcurrentHashMap<>();
72+
private final transient Map<String, Lock> authSuiteJsapiTicketLocker = new ConcurrentHashMap<>();
6273

6374
@Override
6475
public void setBaseApiUrl(String baseUrl) {
@@ -78,6 +89,15 @@ public String getSuiteAccessToken() {
7889
return this.suiteAccessToken;
7990
}
8091

92+
@Override
93+
public WxAccessToken getSuiteAccessTokenEntity() {
94+
WxAccessToken accessToken = new WxAccessToken();
95+
int expiresIn = Math.toIntExact((this.suiteAccessTokenExpiresTime - System.currentTimeMillis()) / 1000L);
96+
accessToken.setExpiresIn(expiresIn <= 0 ? -1 : expiresIn);
97+
accessToken.setAccessToken(this.suiteAccessToken);
98+
return accessToken;
99+
}
100+
81101
public void setSuiteAccessToken(String suiteAccessToken) {
82102
this.suiteAccessToken = suiteAccessToken;
83103
}
@@ -218,12 +238,28 @@ public String getAccessToken(String authCorpId) {
218238
return authCorpAccessTokenMap.get(authCorpId);
219239
}
220240

241+
@Override
242+
public WxAccessToken getAccessTokenEntity(String authCorpId) {
243+
String accessToken = authCorpAccessTokenMap.getOrDefault(authCorpId, StringUtils.EMPTY);
244+
Long expire = authCorpAccessTokenExpireTimeMap.getOrDefault(authCorpId, 0L);
245+
WxAccessToken accessTokenEntity = new WxAccessToken();
246+
accessTokenEntity.setAccessToken(accessToken);
247+
accessTokenEntity.setExpiresIn(Math.toIntExact(expire));
248+
return accessTokenEntity;
249+
}
250+
221251
@Override
222252
public boolean isAccessTokenExpired(String authCorpId) {
223253
return System.currentTimeMillis() > authCorpAccessTokenExpireTimeMap.get(authCorpId);
224254
}
225255

226-
@Override
256+
@Override
257+
public void expireAccessToken(String authCorpId) {
258+
authCorpAccessTokenMap.remove(authCorpId);
259+
authCorpAccessTokenExpireTimeMap.remove(authCorpId);
260+
}
261+
262+
@Override
227263
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
228264
authCorpAccessTokenMap.put(authCorpId, accessToken);
229265
// 预留200秒的时间
@@ -246,6 +282,12 @@ public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
246282
}
247283
}
248284

285+
@Override
286+
public void expireAuthCorpJsApiTicket(String authCorpId) {
287+
this.authCorpJsApiTicketMap.remove(authCorpId);
288+
this.authCorpJsApiTicketExpireTimeMap.remove(authCorpId);
289+
}
290+
249291
@Override
250292
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
251293
// 应该根据不同的授权企业做区分
@@ -269,6 +311,12 @@ public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
269311
}
270312
}
271313

314+
@Override
315+
public void expireAuthSuiteJsApiTicket(String authCorpId) {
316+
this.authSuiteJsApiTicketMap.remove(authCorpId);
317+
this.authSuiteJsApiTicketExpireTimeMap.remove(authCorpId);
318+
}
319+
272320
@Override
273321
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
274322
// 应该根据不同的授权企业做区分
@@ -293,6 +341,16 @@ public String getProviderToken() {
293341
return providerToken;
294342
}
295343

344+
@Override
345+
public WxCpProviderToken getProviderTokenEntity() {
346+
return null;
347+
}
348+
349+
@Override
350+
public void expireProviderToken() {
351+
this.providerTokenExpiresTime = 0L;
352+
}
353+
296354
public void setOauth2redirectUri(String oauth2redirectUri) {
297355
this.oauth2redirectUri = oauth2redirectUri;
298356
}
@@ -343,6 +401,35 @@ public File getTmpDirFile() {
343401
return this.tmpDirFile;
344402
}
345403

404+
@Override
405+
public Lock getProviderAccessTokenLock() {
406+
return this.providerAccessTokenLocker
407+
.computeIfAbsent(String.join(":", this.suiteId, this.corpId), key -> new ReentrantLock());
408+
}
409+
410+
@Override
411+
public Lock getSuiteAccessTokenLock() {
412+
return this.suiteAccessTokenLocker.computeIfAbsent(this.suiteId, key -> new ReentrantLock());
413+
}
414+
415+
@Override
416+
public Lock getAccessTokenLock(String authCorpId) {
417+
return this.accessTokenLocker
418+
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
419+
}
420+
421+
@Override
422+
public Lock getAuthCorpJsapiTicketLock(String authCorpId) {
423+
return this.authCorpJsapiTicketLocker
424+
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
425+
}
426+
427+
@Override
428+
public Lock getSuiteJsapiTicketLock(String authCorpId) {
429+
return this.authSuiteJsapiTicketLocker
430+
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
431+
}
432+
346433
public void setTmpDirFile(File tmpDirFile) {
347434
this.tmpDirFile = tmpDirFile;
348435
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpRedissonConfigImpl.java

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
import me.chanjar.weixin.common.bean.WxAccessToken;
77
import me.chanjar.weixin.common.redis.WxRedisOps;
88
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
9+
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
910
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
1011
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
12+
import org.apache.commons.lang3.StringUtils;
1113

1214
import java.io.File;
1315
import java.io.Serializable;
1416
import java.util.concurrent.TimeUnit;
17+
import java.util.concurrent.locks.Lock;
1518

1619
/**
1720
* 企业微信各种固定、授权配置的Redisson存储实现
@@ -66,6 +69,14 @@ public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializab
6669
*/
6770
private volatile String providerSecret;
6871

72+
// lock key
73+
protected static final String LOCK_KEY = "wechat_tp_lock:";
74+
protected static final String LOCKER_PROVIDER_ACCESS_TOKEN = "providerAccessTokenLock";
75+
protected static final String LOCKER_SUITE_ACCESS_TOKEN = "suiteAccessTokenLock";
76+
protected static final String LOCKER_ACCESS_TOKEN = "accessTokenLock";
77+
protected static final String LOCKER_CORP_JSAPI_TICKET = "corpJsapiTicketLock";
78+
protected static final String LOCKER_SUITE_JSAPI_TICKET = "suiteJsapiTicketLock";
79+
6980
@Override
7081
public void setBaseApiUrl(String baseUrl) {
7182
this.baseApiUrl = baseUrl;
@@ -88,6 +99,20 @@ public String getSuiteAccessToken() {
8899
return wxRedisOps.getValue(keyWithPrefix(suiteAccessTokenKey));
89100
}
90101

102+
@Override
103+
public WxAccessToken getSuiteAccessTokenEntity() {
104+
String suiteAccessToken = wxRedisOps.getValue(keyWithPrefix(suiteAccessTokenKey));
105+
Long expireIn = wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey));
106+
if (StringUtils.isBlank(suiteAccessToken) || expireIn == null || expireIn == 0 || expireIn == -2) {
107+
return new WxAccessToken();
108+
}
109+
110+
WxAccessToken suiteAccessTokenEntity = new WxAccessToken();
111+
suiteAccessTokenEntity.setAccessToken(suiteAccessToken);
112+
suiteAccessTokenEntity.setExpiresIn(Math.max(Math.toIntExact(expireIn), 0));
113+
return suiteAccessTokenEntity;
114+
}
115+
91116
@Override
92117
public boolean isSuiteAccessTokenExpired() {
93118
//remain time to live in seconds, or key not exist
@@ -185,13 +210,32 @@ public String getAccessToken(String authCorpId) {
185210
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + accessTokenKey);
186211
}
187212

213+
@Override
214+
public WxAccessToken getAccessTokenEntity(String authCorpId) {
215+
String accessToken = wxRedisOps.getValue(keyWithPrefix(authCorpId) + accessTokenKey);
216+
Long expire = wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey);
217+
if (StringUtils.isBlank(accessToken) || expire == null || expire == 0 || expire == -2) {
218+
return new WxAccessToken();
219+
}
220+
221+
WxAccessToken accessTokenEntity = new WxAccessToken();
222+
accessTokenEntity.setAccessToken(accessToken);
223+
accessTokenEntity.setExpiresIn(Math.max(Math.toIntExact(expire), 0));
224+
return accessTokenEntity;
225+
}
226+
188227
@Override
189228
public boolean isAccessTokenExpired(String authCorpId) {
190229
//没有设置或者TTL为0,都是过期
191230
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == 0L
192231
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == -2;
193232
}
194233

234+
@Override
235+
public void expireAccessToken(String authCorpId) {
236+
wxRedisOps.expire(keyWithPrefix(authCorpId) + accessTokenKey, 0, TimeUnit.SECONDS);
237+
}
238+
195239
@Override
196240
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
197241
wxRedisOps.setValue(keyWithPrefix(authCorpId) + accessTokenKey, accessToken, expiredInSeconds, TimeUnit.SECONDS);
@@ -213,6 +257,11 @@ public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
213257
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == -2;
214258
}
215259

260+
@Override
261+
public void expireAuthCorpJsApiTicket(String authCorpId) {
262+
wxRedisOps.expire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, 0, TimeUnit.SECONDS);
263+
}
264+
216265
@Override
217266
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
218267
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, jsApiTicket, expiredInSeconds,
@@ -235,6 +284,11 @@ public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
235284
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == -2;
236285
}
237286

287+
@Override
288+
public void expireAuthSuiteJsApiTicket(String authCorpId) {
289+
wxRedisOps.expire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, 0, TimeUnit.SECONDS);
290+
}
291+
238292
@Override
239293
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
240294
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, jsApiTicket, expiredInSeconds,
@@ -257,6 +311,25 @@ public String getProviderToken() {
257311
return wxRedisOps.getValue(keyWithPrefix(providerTokenKey));
258312
}
259313

314+
@Override
315+
public WxCpProviderToken getProviderTokenEntity() {
316+
String providerToken = wxRedisOps.getValue(keyWithPrefix(providerTokenKey));
317+
Long expire = wxRedisOps.getExpire(keyWithPrefix(providerTokenKey));
318+
319+
if (StringUtils.isBlank(providerToken) || expire == null || expire == 0 || expire == -2) {
320+
return new WxCpProviderToken();
321+
}
322+
323+
WxCpProviderToken wxCpProviderToken = new WxCpProviderToken();
324+
wxCpProviderToken.setProviderAccessToken(providerToken);
325+
wxCpProviderToken.setExpiresIn(Math.max(Math.toIntExact(expire), 0));
326+
return wxCpProviderToken;
327+
}
328+
329+
@Override
330+
public void expireProviderToken() {
331+
wxRedisOps.expire(keyWithPrefix(providerTokenKey), 0, TimeUnit.SECONDS);
332+
}
260333

261334
/**
262335
* 网络代理相关
@@ -286,6 +359,37 @@ public File getTmpDirFile() {
286359
return tmpDirFile;
287360
}
288361

362+
@Override
363+
public Lock getProviderAccessTokenLock() {
364+
return getLockByKey(String.join(":", this.corpId, LOCKER_PROVIDER_ACCESS_TOKEN));
365+
}
366+
367+
@Override
368+
public Lock getSuiteAccessTokenLock() {
369+
return getLockByKey(LOCKER_SUITE_ACCESS_TOKEN);
370+
}
371+
372+
@Override
373+
public Lock getAccessTokenLock(String authCorpId) {
374+
return getLockByKey(String.join(":", authCorpId, LOCKER_ACCESS_TOKEN));
375+
}
376+
377+
@Override
378+
public Lock getAuthCorpJsapiTicketLock(String authCorpId) {
379+
return getLockByKey(String.join(":", authCorpId, LOCKER_CORP_JSAPI_TICKET));
380+
}
381+
382+
@Override
383+
public Lock getSuiteJsapiTicketLock(String authCorpId) {
384+
return getLockByKey(String.join(":", authCorpId, LOCKER_SUITE_JSAPI_TICKET));
385+
}
386+
387+
private Lock getLockByKey(String key) {
388+
// 最终key的模式:(keyPrefix:)wechat_tp_lock:suiteId:(authCorpId):lockKey
389+
// 其中keyPrefix目前不支持外部配置,authCorpId只有涉及到corpAccessToken, suiteJsapiTicket, authCorpJsapiTicket时才会拼上
390+
return this.wxRedisOps.getLock(String.join(":", keyWithPrefix(LOCK_KEY + this.suiteId), key));
391+
}
392+
289393
@Override
290394
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
291395
return this.apacheHttpClientBuilder;

0 commit comments

Comments
 (0)
0