Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.bean.WxCpProviderToken;

import java.io.File;
import java.util.concurrent.locks.Lock;

/**
* 微信客户端(第三方应用)配置存储
Expand All @@ -30,6 +32,11 @@ public interface WxCpTpConfigStorage {
* 第三方应用的suite access token相关
*/
String getSuiteAccessToken();
/**
* 获取suite_access_token和剩余过期时间
* @return suite access token and the remaining expiration time
*/
WxAccessToken getSuiteAccessTokenEntity();
boolean isSuiteAccessTokenExpired();
//强制将suite access token过期掉.
void expireSuiteAccessToken();
Expand Down Expand Up @@ -71,27 +78,34 @@ public interface WxCpTpConfigStorage {
* 授权企业的access token相关
*/
String getAccessToken(String authCorpId);
WxAccessToken getAccessTokenEntity(String authCorpId);
boolean isAccessTokenExpired(String authCorpId);
void expireAccessToken(String authCorpId);
void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds);

/**
* 授权企业的js api ticket相关
*/
String getAuthCorpJsApiTicket(String authCorpId);
boolean isAuthCorpJsApiTicketExpired(String authCorpId);
void expireAuthCorpJsApiTicket(String authCorpId);
void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);

/**
* 授权企业的第三方应用js api ticket相关
*/
String getAuthSuiteJsApiTicket(String authCorpId);
boolean isAuthSuiteJsApiTicketExpired(String authCorpId);
void expireAuthSuiteJsApiTicket(String authCorpId);
void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);;

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

String getProviderToken();
WxCpProviderToken getProviderTokenEntity();
// 强制过期
void expireProviderToken();

/**
* 网络代理相关
Expand All @@ -108,4 +122,9 @@ public interface WxCpTpConfigStorage {
@Deprecated
File getTmpDirFile();

Lock getProviderAccessTokenLock();
Lock getSuiteAccessTokenLock();
Lock getAccessTokenLock(String authCorpId);
Lock getAuthCorpJsapiTicketLock(String authCorpId);
Lock getSuiteJsapiTicketLock(String authCorpId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

private volatile String baseApiUrl;

// locker
private final transient Map<String, Lock> providerAccessTokenLocker = new ConcurrentHashMap<>();
private final transient Map<String, Lock> suiteAccessTokenLocker = new ConcurrentHashMap<>();
private final transient Map<String, Lock> accessTokenLocker = new ConcurrentHashMap<>();
private final transient Map<String, Lock> authCorpJsapiTicketLocker = new ConcurrentHashMap<>();
private final transient Map<String, Lock> authSuiteJsapiTicketLocker = new ConcurrentHashMap<>();

@Override
public void setBaseApiUrl(String baseUrl) {
Expand All @@ -78,6 +89,15 @@ public String getSuiteAccessToken() {
return this.suiteAccessToken;
}

@Override
public WxAccessToken getSuiteAccessTokenEntity() {
WxAccessToken accessToken = new WxAccessToken();
int expiresIn = Math.toIntExact((this.suiteAccessTokenExpiresTime - System.currentTimeMillis()) / 1000L);
accessToken.setExpiresIn(expiresIn <= 0 ? -1 : expiresIn);
accessToken.setAccessToken(this.suiteAccessToken);
return accessToken;
}

public void setSuiteAccessToken(String suiteAccessToken) {
this.suiteAccessToken = suiteAccessToken;
}
Expand Down Expand Up @@ -218,12 +238,28 @@ public String getAccessToken(String authCorpId) {
return authCorpAccessTokenMap.get(authCorpId);
}

@Override
public WxAccessToken getAccessTokenEntity(String authCorpId) {
String accessToken = authCorpAccessTokenMap.getOrDefault(authCorpId, StringUtils.EMPTY);
Long expire = authCorpAccessTokenExpireTimeMap.getOrDefault(authCorpId, 0L);
WxAccessToken accessTokenEntity = new WxAccessToken();
accessTokenEntity.setAccessToken(accessToken);
accessTokenEntity.setExpiresIn(Math.toIntExact(expire));
return accessTokenEntity;
}

@Override
public boolean isAccessTokenExpired(String authCorpId) {
return System.currentTimeMillis() > authCorpAccessTokenExpireTimeMap.get(authCorpId);
}

@Override
@Override
public void expireAccessToken(String authCorpId) {
authCorpAccessTokenMap.remove(authCorpId);
authCorpAccessTokenExpireTimeMap.remove(authCorpId);
}

@Override
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
authCorpAccessTokenMap.put(authCorpId, accessToken);
// 预留200秒的时间
Expand All @@ -246,6 +282,12 @@ public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
}
}

@Override
public void expireAuthCorpJsApiTicket(String authCorpId) {
this.authCorpJsApiTicketMap.remove(authCorpId);
this.authCorpJsApiTicketExpireTimeMap.remove(authCorpId);
}

@Override
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
// 应该根据不同的授权企业做区分
Expand All @@ -269,6 +311,12 @@ public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
}
}

@Override
public void expireAuthSuiteJsApiTicket(String authCorpId) {
this.authSuiteJsApiTicketMap.remove(authCorpId);
this.authSuiteJsApiTicketExpireTimeMap.remove(authCorpId);
}

@Override
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
// 应该根据不同的授权企业做区分
Expand All @@ -293,6 +341,16 @@ public String getProviderToken() {
return providerToken;
}

@Override
public WxCpProviderToken getProviderTokenEntity() {
return null;
}

@Override
public void expireProviderToken() {
this.providerTokenExpiresTime = 0L;
}

public void setOauth2redirectUri(String oauth2redirectUri) {
this.oauth2redirectUri = oauth2redirectUri;
}
Expand Down Expand Up @@ -343,6 +401,35 @@ public File getTmpDirFile() {
return this.tmpDirFile;
}

@Override
public Lock getProviderAccessTokenLock() {
return this.providerAccessTokenLocker
.computeIfAbsent(String.join(":", this.suiteId, this.corpId), key -> new ReentrantLock());
}

@Override
public Lock getSuiteAccessTokenLock() {
return this.suiteAccessTokenLocker.computeIfAbsent(this.suiteId, key -> new ReentrantLock());
}

@Override
public Lock getAccessTokenLock(String authCorpId) {
return this.accessTokenLocker
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
}

@Override
public Lock getAuthCorpJsapiTicketLock(String authCorpId) {
return this.authCorpJsapiTicketLocker
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
}

@Override
public Lock getSuiteJsapiTicketLock(String authCorpId) {
return this.authSuiteJsapiTicketLocker
.computeIfAbsent(String.join(":", this.suiteId, authCorpId), key -> new ReentrantLock());
}

public void setTmpDirFile(File tmpDirFile) {
this.tmpDirFile = tmpDirFile;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.redis.WxRedisOps;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

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

// lock key
protected static final String LOCK_KEY = "wechat_tp_lock:";
protected static final String LOCKER_PROVIDER_ACCESS_TOKEN = "providerAccessTokenLock";
protected static final String LOCKER_SUITE_ACCESS_TOKEN = "suiteAccessTokenLock";
protected static final String LOCKER_ACCESS_TOKEN = "accessTokenLock";
protected static final String LOCKER_CORP_JSAPI_TICKET = "corpJsapiTicketLock";
protected static final String LOCKER_SUITE_JSAPI_TICKET = "suiteJsapiTicketLock";

@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
Expand All @@ -88,6 +99,20 @@ public String getSuiteAccessToken() {
return wxRedisOps.getValue(keyWithPrefix(suiteAccessTokenKey));
}

@Override
public WxAccessToken getSuiteAccessTokenEntity() {
String suiteAccessToken = wxRedisOps.getValue(keyWithPrefix(suiteAccessTokenKey));
Long expireIn = wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey));
if (StringUtils.isBlank(suiteAccessToken) || expireIn == null || expireIn == 0 || expireIn == -2) {
return new WxAccessToken();
}

WxAccessToken suiteAccessTokenEntity = new WxAccessToken();
suiteAccessTokenEntity.setAccessToken(suiteAccessToken);
suiteAccessTokenEntity.setExpiresIn(Math.max(Math.toIntExact(expireIn), 0));
return suiteAccessTokenEntity;
}

@Override
public boolean isSuiteAccessTokenExpired() {
//remain time to live in seconds, or key not exist
Expand Down Expand Up @@ -185,13 +210,32 @@ public String getAccessToken(String authCorpId) {
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + accessTokenKey);
}

@Override
public WxAccessToken getAccessTokenEntity(String authCorpId) {
String accessToken = wxRedisOps.getValue(keyWithPrefix(authCorpId) + accessTokenKey);
Long expire = wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey);
if (StringUtils.isBlank(accessToken) || expire == null || expire == 0 || expire == -2) {
return new WxAccessToken();
}

WxAccessToken accessTokenEntity = new WxAccessToken();
accessTokenEntity.setAccessToken(accessToken);
accessTokenEntity.setExpiresIn(Math.max(Math.toIntExact(expire), 0));
return accessTokenEntity;
}

@Override
public boolean isAccessTokenExpired(String authCorpId) {
//没有设置或者TTL为0,都是过期
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == 0L
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == -2;
}

@Override
public void expireAccessToken(String authCorpId) {
wxRedisOps.expire(keyWithPrefix(authCorpId) + accessTokenKey, 0, TimeUnit.SECONDS);
}

@Override
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(authCorpId) + accessTokenKey, accessToken, expiredInSeconds, TimeUnit.SECONDS);
Expand All @@ -213,6 +257,11 @@ public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == -2;
}

@Override
public void expireAuthCorpJsApiTicket(String authCorpId) {
wxRedisOps.expire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, 0, TimeUnit.SECONDS);
}

@Override
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, jsApiTicket, expiredInSeconds,
Expand All @@ -235,6 +284,11 @@ public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == -2;
}

@Override
public void expireAuthSuiteJsApiTicket(String authCorpId) {
wxRedisOps.expire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, 0, TimeUnit.SECONDS);
}

@Override
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, jsApiTicket, expiredInSeconds,
Expand All @@ -257,6 +311,25 @@ public String getProviderToken() {
return wxRedisOps.getValue(keyWithPrefix(providerTokenKey));
}

@Override
public WxCpProviderToken getProviderTokenEntity() {
String providerToken = wxRedisOps.getValue(keyWithPrefix(providerTokenKey));
Long expire = wxRedisOps.getExpire(keyWithPrefix(providerTokenKey));

if (StringUtils.isBlank(providerToken) || expire == null || expire == 0 || expire == -2) {
return new WxCpProviderToken();
}

WxCpProviderToken wxCpProviderToken = new WxCpProviderToken();
wxCpProviderToken.setProviderAccessToken(providerToken);
wxCpProviderToken.setExpiresIn(Math.max(Math.toIntExact(expire), 0));
return wxCpProviderToken;
}

@Override
public void expireProviderToken() {
wxRedisOps.expire(keyWithPrefix(providerTokenKey), 0, TimeUnit.SECONDS);
}

/**
* 网络代理相关
Expand Down Expand Up @@ -286,6 +359,37 @@ public File getTmpDirFile() {
return tmpDirFile;
}

@Override
public Lock getProviderAccessTokenLock() {
return getLockByKey(String.join(":", this.corpId, LOCKER_PROVIDER_ACCESS_TOKEN));
}

@Override
public Lock getSuiteAccessTokenLock() {
return getLockByKey(LOCKER_SUITE_ACCESS_TOKEN);
}

@Override
public Lock getAccessTokenLock(String authCorpId) {
return getLockByKey(String.join(":", authCorpId, LOCKER_ACCESS_TOKEN));
}

@Override
public Lock getAuthCorpJsapiTicketLock(String authCorpId) {
return getLockByKey(String.join(":", authCorpId, LOCKER_CORP_JSAPI_TICKET));
}

@Override
public Lock getSuiteJsapiTicketLock(String authCorpId) {
return getLockByKey(String.join(":", authCorpId, LOCKER_SUITE_JSAPI_TICKET));
}

private Lock getLockByKey(String key) {
// 最终key的模式:(keyPrefix:)wechat_tp_lock:suiteId:(authCorpId):lockKey
// 其中keyPrefix目前不支持外部配置,authCorpId只有涉及到corpAccessToken, suiteJsapiTicket, authCorpJsapiTicket时才会拼上
return this.wxRedisOps.getLock(String.join(":", keyWithPrefix(LOCK_KEY + this.suiteId), key));
}

@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return this.apacheHttpClientBuilder;
Expand Down
Loading