🆕 #3703 【企业微信】第三方应用基础接口实现

This commit is contained in:
graalvm-samples
2025-09-23 00:25:39 +08:00
committed by GitHub
parent 213cf6fd67
commit 88bdd4a36e
35 changed files with 1730 additions and 492 deletions

View File

@@ -793,4 +793,6 @@ public class WxCpTpXmlMessage implements Serializable {
log.debug("解密后的原始xml消息内容{}", plainText);
return fromXml(plainText);
}
}

View File

@@ -130,7 +130,7 @@ public interface WxCpTpConfigStorage {
* @return the aes key
*/
//第三方应用的EncodingAESKey用来检查签名
String getAesKey();
String getEncodingAESKey();
/**
* 企微服务商企业ID & 企业secret

View File

@@ -0,0 +1,431 @@
package me.chanjar.weixin.cp.config.impl;
import lombok.Builder;
import lombok.NonNull;
import lombok.Setter;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.redis.RedissonWxRedisOps;
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.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import java.io.File;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 企业微信各种固定、授权配置的Redisson存储实现
*/
public abstract class AbstractWxCpTpInRedisConfigImpl extends WxCpTpDefaultConfigImpl implements Serializable {
private static final long serialVersionUID = -5385639031981770319L;
public AbstractWxCpTpInRedisConfigImpl(@NonNull WxRedisOps wxRedisOps) {
this(wxRedisOps, null);
}
public AbstractWxCpTpInRedisConfigImpl(@NonNull WxRedisOps wxRedisOps, String keyPrefix) {
this.wxRedisOps = wxRedisOps;
this.keyPrefix = keyPrefix;
}
/**
* The constant LOCK_KEY.
*/
// lock key
protected static final String LOCK_KEY = "wechat_tp_lock:";
/**
* The constant LOCKER_PROVIDER_ACCESS_TOKEN.
*/
protected static final String LOCKER_PROVIDER_ACCESS_TOKEN = "providerAccessTokenLock";
/**
* The constant LOCKER_SUITE_ACCESS_TOKEN.
*/
protected static final String LOCKER_SUITE_ACCESS_TOKEN = "suiteAccessTokenLock";
/**
* The constant LOCKER_ACCESS_TOKEN.
*/
protected static final String LOCKER_ACCESS_TOKEN = "accessTokenLock";
/**
* The constant LOCKER_CORP_JSAPI_TICKET.
*/
protected static final String LOCKER_CORP_JSAPI_TICKET = "corpJsapiTicketLock";
/**
* The constant LOCKER_SUITE_JSAPI_TICKET.
*/
protected static final String LOCKER_SUITE_JSAPI_TICKET = "suiteJsapiTicketLock";
@NonNull
private final WxRedisOps wxRedisOps;
private final String suiteAccessTokenKey = ":suiteAccessTokenKey:";
private final String suiteTicketKey = ":suiteTicketKey:";
private final String accessTokenKey = ":accessTokenKey:";
private final String authCorpJsApiTicketKey = ":authCorpJsApiTicketKey:";
private final String authSuiteJsApiTicketKey = ":authSuiteJsApiTicketKey:";
private final String providerTokenKey = ":providerTokenKey:";
/**
* redis里面key的统一前缀
*/
@Setter
private String keyPrefix = "";
private volatile String baseApiUrl;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
private volatile String httpProxyPassword;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
private volatile File tmpDirFile;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}
@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = "https://qyapi.weixin.qq.com";
}
return baseApiUrl + path;
}
/**
* 第三方应用的suite access token相关
*/
@Override
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
return wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == -2;
}
@Override
public void expireSuiteAccessToken() {
wxRedisOps.expire(keyWithPrefix(suiteAccessTokenKey), 0, TimeUnit.SECONDS);
}
@Override
public void updateSuiteAccessToken(WxAccessToken suiteAccessToken) {
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
}
@Override
public void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) {
wxRedisOps.setValue(keyWithPrefix(suiteAccessTokenKey), suiteAccessToken, expiresInSeconds, TimeUnit.SECONDS);
}
/**
* 第三方应用的suite ticket相关
*/
@Override
public String getSuiteTicket() {
return wxRedisOps.getValue(keyWithPrefix(suiteTicketKey));
}
@Override
public boolean isSuiteTicketExpired() {
//remain time to live in seconds, or key not exist
return wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == -2;
}
@Override
public void expireSuiteTicket() {
wxRedisOps.expire(keyWithPrefix(suiteTicketKey), 0, TimeUnit.SECONDS);
}
@Override
public void updateSuiteTicket(String suiteTicket, int expiresInSeconds) {
wxRedisOps.setValue(keyWithPrefix(suiteTicketKey), suiteTicket, expiresInSeconds, TimeUnit.SECONDS);
}
/**
* 第三方应用的其他配置,来自于企微配置
*/
@Override
public String getSuiteId() {
return suiteId;
}
@Override
public String getSuiteSecret() {
return suiteSecret;
}
// 第三方应用的token用来检查应用的签名
@Override
public String getToken() {
return token;
}
//第三方应用的EncodingAESKey用来检查签名
@Override
public String getEncodingAESKey() {
return encodingAESKey;
}
/**
* 企微服务商企业ID & 企业secret, 来自于企微配置
*/
@Override
public String getCorpId() {
return corpId;
}
@Override
public String getProviderSecret() {
return providerSecret;
}
@Override
public void setProviderSecret(String providerSecret) {
this.providerSecret = providerSecret;
}
/**
* 授权企业的access token相关
*/
@Override
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((int) ((expire - System.currentTimeMillis()) / 1000 + 200));
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);
}
/**
* 授权企业的js api ticket相关
*/
@Override
public String getAuthCorpJsApiTicket(String authCorpId) {
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey);
}
@Override
public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
//没有设置或TTL为0,都是过期
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == 0L
|| 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,
TimeUnit.SECONDS);
}
/**
* 授权企业的第三方应用js api ticket相关
*/
@Override
public String getAuthSuiteJsApiTicket(String authCorpId) {
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey);
}
@Override
public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
//没有设置或者TTL为0都是过期
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == 0L
|| 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,
TimeUnit.SECONDS);
}
@Override
public boolean isProviderTokenExpired() {
//remain time to live in seconds, or key not exist
return wxRedisOps.getExpire(providerKeyWithPrefix(providerTokenKey)) == 0L || wxRedisOps.getExpire(providerKeyWithPrefix(providerTokenKey)) == -2;
}
@Override
public void updateProviderToken(String providerToken, int expiredInSeconds) {
wxRedisOps.setValue(providerKeyWithPrefix(providerTokenKey), providerToken, expiredInSeconds, TimeUnit.SECONDS);
}
@Override
public String getProviderToken() {
return wxRedisOps.getValue(providerKeyWithPrefix(providerTokenKey));
}
@Override
public WxCpProviderToken getProviderTokenEntity() {
String providerToken = wxRedisOps.getValue(providerKeyWithPrefix(providerTokenKey));
Long expire = wxRedisOps.getExpire(providerKeyWithPrefix(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(providerKeyWithPrefix(providerTokenKey), 0, TimeUnit.SECONDS);
}
/**
* 网络代理相关
*/
@Override
public String getHttpProxyHost() {
return this.httpProxyHost;
}
@Override
public int getHttpProxyPort() {
return this.httpProxyPort;
}
@Override
public String getHttpProxyUsername() {
return this.httpProxyUsername;
}
@Override
public String getHttpProxyPassword() {
return this.httpProxyPassword;
}
@Override
public File getTmpDirFile() {
return tmpDirFile;
}
@Override
public Lock getProviderAccessTokenLock() {
return getProviderLockByKey(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));
}
/**
* 单独处理provider,且不应和suite 有关系
*/
private Lock getProviderLockByKey(String key) {
return this.wxRedisOps.getLock(String.join(":", providerKeyWithPrefix(LOCK_KEY), key));
}
@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return this.apacheHttpClientBuilder;
}
@Override
public boolean autoRefreshToken() {
return false;
}
@Override
public String toString() {
return WxCpGsonBuilder.create().toJson(this);
}
/**
* 一个provider 会有多个suite,需要唯一标识作为前缀
*/
private String keyWithPrefix(String key) {
return keyPrefix + ":" + suiteId + ":" + key;
}
/**
* provider 应该独享一个key,且不和任何suite关联
* 一个provider 会有多个suite,不同的suite 都应该指向同一个provider 的数据
*/
private String providerKeyWithPrefix(String key) {
return keyPrefix + ":" + corpId + ":" + key;
}
}

View File

@@ -28,20 +28,32 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
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<>();
private volatile String corpId;
private volatile String corpSecret;
/**
* 企微服务商企业ID & 企业secret来自于企微配置
*/
protected volatile String corpId;
/**
* 服务商secret
*/
private volatile String providerSecret;
protected volatile String providerSecret;
private volatile String providerToken;
private volatile long providerTokenExpiresTime;
private volatile String suiteId;
private volatile String suiteSecret;
private volatile String token;
/**
* 第三方应用的其他配置,来自于企微配置
*/
protected volatile String suiteId;
protected volatile String suiteSecret;
/**
* 第三方应用的token用来检查应用的签名
*/
protected volatile String token;
private volatile String suiteAccessToken;
private volatile long suiteAccessTokenExpiresTime;
private volatile String aesKey;
/**
* 第三方应用的EncodingAESKey用来检查签名
*/
protected volatile String encodingAESKey;
private volatile String suiteTicket;
private volatile long suiteTicketExpiresTime;
private volatile String oauth2redirectUri;
@@ -186,11 +198,10 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
/**
* Sets suite id.
*
* @param corpId the corp id
* @param suiteId
*/
@Deprecated
public void setSuiteId(String corpId) {
this.suiteId = corpId;
public void setSuiteId(String suiteId) {
this.suiteId = suiteId;
}
@Override
@@ -200,10 +211,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
/**
* Sets suite secret.
*
* @param corpSecret the corp secret
*/
@Deprecated
public void setSuiteSecret(String corpSecret) {
this.suiteSecret = corpSecret;
}
@@ -218,24 +226,22 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
*
* @param token the token
*/
@Deprecated
public void setToken(String token) {
this.token = token;
}
@Override
public String getAesKey() {
return this.aesKey;
public String getEncodingAESKey() {
return this.encodingAESKey;
}
/**
* Sets aes key.
* Sets aes key. encodingAESKey
*
* @param aesKey the aes key
* @param encodingAESKey the aes key
*/
@Deprecated
public void setAesKey(String aesKey) {
this.aesKey = aesKey;
public void setEncodingAESKey(String encodingAESKey) {
this.encodingAESKey = encodingAESKey;
}
@@ -249,25 +255,15 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
*
* @param corpId the corp id
*/
@Deprecated
public void setCorpId(String corpId) {
this.corpId = corpId;
}
@Override
public String getCorpSecret() {
return this.corpSecret;
return this.providerSecret;
}
/**
* Sets corp secret.
*
* @param corpSecret the corp secret
*/
@Deprecated
public void setCorpSecret(String corpSecret) {
this.corpSecret = corpSecret;
}
@Override
public void setProviderSecret(String providerSecret) {

View File

@@ -0,0 +1,24 @@
package me.chanjar.weixin.cp.config.impl;
import lombok.NonNull;
import me.chanjar.weixin.common.redis.JedisWxRedisOps;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.util.Pool;
/**
* 基于 jedis 的实现
*
* @author yl
* created on 2023/04/23
*/
public class WxCpTpJedisConfigImpl extends AbstractWxCpTpInRedisConfigImpl {
private static final long serialVersionUID = -1869372247414407433L;
public WxCpTpJedisConfigImpl(Pool<Jedis> jedisPool) {
this(jedisPool, null);
}
public WxCpTpJedisConfigImpl(@NonNull Pool<Jedis> jedisPool, String keyPrefix) {
super(new JedisWxRedisOps(jedisPool), keyPrefix);
}
}

View File

@@ -0,0 +1,24 @@
package me.chanjar.weixin.cp.config.impl;
import lombok.Builder;
import lombok.NonNull;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 基于 RedisTemplate 的实现
*
* @author yl
* created on 2023/04/23
*/
public class WxCpTpRedisTemplateConfigImpl extends AbstractWxCpTpInRedisConfigImpl {
private static final long serialVersionUID = -1660004125413310620L;
public WxCpTpRedisTemplateConfigImpl(@NonNull StringRedisTemplate stringRedisTemplate) {
this(stringRedisTemplate, null);
}
public WxCpTpRedisTemplateConfigImpl(@NonNull StringRedisTemplate stringRedisTemplate, String keyPrefix) {
super(new RedisTemplateWxRedisOps(stringRedisTemplate), keyPrefix);
}
}

View File

@@ -1,449 +1,25 @@
package me.chanjar.weixin.cp.config.impl;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import lombok.Setter;
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;
import me.chanjar.weixin.common.redis.RedissonWxRedisOps;
import org.redisson.api.RedissonClient;
/**
* 企业微信各种固定、授权配置的Redisson存储实现
* 基于Redisson实现
*
* @author yuanqixun created on 2020 /5/13
* @author yl
*/
@Builder
public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializable {
private static final long serialVersionUID = -5385639031981770319L;
public class WxCpTpRedissonConfigImpl extends AbstractWxCpTpInRedisConfigImpl {
private static final long serialVersionUID = -5674792341070783967L;
/**
* The constant LOCK_KEY.
*/
// lock key
protected static final String LOCK_KEY = "wechat_tp_lock:";
/**
* The constant LOCKER_PROVIDER_ACCESS_TOKEN.
*/
protected static final String LOCKER_PROVIDER_ACCESS_TOKEN = "providerAccessTokenLock";
/**
* The constant LOCKER_SUITE_ACCESS_TOKEN.
*/
protected static final String LOCKER_SUITE_ACCESS_TOKEN = "suiteAccessTokenLock";
/**
* The constant LOCKER_ACCESS_TOKEN.
*/
protected static final String LOCKER_ACCESS_TOKEN = "accessTokenLock";
/**
* The constant LOCKER_CORP_JSAPI_TICKET.
*/
protected static final String LOCKER_CORP_JSAPI_TICKET = "corpJsapiTicketLock";
/**
* The constant LOCKER_SUITE_JSAPI_TICKET.
*/
protected static final String LOCKER_SUITE_JSAPI_TICKET = "suiteJsapiTicketLock";
@NonNull
private final WxRedisOps wxRedisOps;
private final String suiteAccessTokenKey = ":suiteAccessTokenKey:";
private final String suiteTicketKey = ":suiteTicketKey:";
private final String accessTokenKey = ":accessTokenKey:";
private final String authCorpJsApiTicketKey = ":authCorpJsApiTicketKey:";
private final String authSuiteJsApiTicketKey = ":authSuiteJsApiTicketKey:";
private final String providerTokenKey = ":providerTokenKey:";
/**
* redis里面key的统一前缀
*/
@Setter
private String keyPrefix = "";
private volatile String baseApiUrl;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
private volatile String httpProxyPassword;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
private volatile File tmpDirFile;
/**
* 第三方应用的其他配置,来自于企微配置
*/
private volatile String suiteId;
private volatile String suiteSecret;
/**
* 第三方应用的token用来检查应用的签名
*/
private volatile String token;
/**
* 第三方应用的EncodingAESKey用来检查签名
*/
private volatile String aesKey;
/**
* 企微服务商企业ID & 企业secret来自于企微配置
*/
private volatile String corpId;
private volatile String corpSecret;
/**
* 服务商secret
*/
private volatile String providerSecret;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
public WxCpTpRedissonConfigImpl(@NonNull RedissonClient redissonClient) {
this(redissonClient, null);
}
@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = "https://qyapi.weixin.qq.com";
}
return baseApiUrl + path;
}
/**
* 第三方应用的suite access token相关
*/
@Override
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
return wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == -2;
}
@Override
public void expireSuiteAccessToken() {
wxRedisOps.expire(keyWithPrefix(suiteAccessTokenKey), 0, TimeUnit.SECONDS);
}
@Override
public void updateSuiteAccessToken(WxAccessToken suiteAccessToken) {
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
}
@Override
public void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) {
wxRedisOps.setValue(keyWithPrefix(suiteAccessTokenKey), suiteAccessToken, expiresInSeconds, TimeUnit.SECONDS);
}
/**
* 第三方应用的suite ticket相关
*/
@Override
public String getSuiteTicket() {
return wxRedisOps.getValue(keyWithPrefix(suiteTicketKey));
}
@Override
public boolean isSuiteTicketExpired() {
//remain time to live in seconds, or key not exist
return wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == -2;
}
@Override
public void expireSuiteTicket() {
wxRedisOps.expire(keyWithPrefix(suiteTicketKey), 0, TimeUnit.SECONDS);
}
@Override
public void updateSuiteTicket(String suiteTicket, int expiresInSeconds) {
wxRedisOps.setValue(keyWithPrefix(suiteTicketKey), suiteTicket, expiresInSeconds, TimeUnit.SECONDS);
}
/**
* 第三方应用的其他配置,来自于企微配置
*/
@Override
public String getSuiteId() {
return suiteId;
}
@Override
public String getSuiteSecret() {
return suiteSecret;
}
// 第三方应用的token用来检查应用的签名
@Override
public String getToken() {
return token;
}
//第三方应用的EncodingAESKey用来检查签名
@Override
public String getAesKey() {
return aesKey;
}
/**
* 企微服务商企业ID & 企业secret, 来自于企微配置
*/
@Override
public String getCorpId() {
return corpId;
}
@Override
public String getCorpSecret() {
return corpSecret;
}
@Override
public void setProviderSecret(String providerSecret) {
this.providerSecret = providerSecret;
}
@Override
public String getProviderSecret() {
return providerSecret;
}
/**
* 授权企业的access token相关
*/
@Override
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((int) ((expire - System.currentTimeMillis()) / 1000 + 200));
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);
}
/**
* 授权企业的js api ticket相关
*/
@Override
public String getAuthCorpJsApiTicket(String authCorpId) {
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey);
}
@Override
public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
//没有设置或TTL为0,都是过期
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == 0L
|| 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,
TimeUnit.SECONDS);
}
/**
* 授权企业的第三方应用js api ticket相关
*/
@Override
public String getAuthSuiteJsApiTicket(String authCorpId) {
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey);
}
@Override
public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
//没有设置或者TTL为0都是过期
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == 0L
|| 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,
TimeUnit.SECONDS);
}
@Override
public boolean isProviderTokenExpired() {
//remain time to live in seconds, or key not exist
return wxRedisOps.getExpire(providerKeyWithPrefix(providerTokenKey)) == 0L || wxRedisOps.getExpire(providerKeyWithPrefix(providerTokenKey)) == -2;
}
@Override
public void updateProviderToken(String providerToken, int expiredInSeconds) {
wxRedisOps.setValue(providerKeyWithPrefix(providerTokenKey), providerToken, expiredInSeconds, TimeUnit.SECONDS);
}
@Override
public String getProviderToken() {
return wxRedisOps.getValue(providerKeyWithPrefix(providerTokenKey));
}
@Override
public WxCpProviderToken getProviderTokenEntity() {
String providerToken = wxRedisOps.getValue(providerKeyWithPrefix(providerTokenKey));
Long expire = wxRedisOps.getExpire(providerKeyWithPrefix(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(providerKeyWithPrefix(providerTokenKey), 0, TimeUnit.SECONDS);
}
/**
* 网络代理相关
*/
@Override
public String getHttpProxyHost() {
return this.httpProxyHost;
}
@Override
public int getHttpProxyPort() {
return this.httpProxyPort;
}
@Override
public String getHttpProxyUsername() {
return this.httpProxyUsername;
}
@Override
public String getHttpProxyPassword() {
return this.httpProxyPassword;
}
@Override
public File getTmpDirFile() {
return tmpDirFile;
}
@Override
public Lock getProviderAccessTokenLock() {
return getProviderLockByKey(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));
}
/**
* 单独处理provider,且不应和suite 有关系
*/
private Lock getProviderLockByKey(String key) {
return this.wxRedisOps.getLock(String.join(":", providerKeyWithPrefix(LOCK_KEY), key));
}
@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return this.apacheHttpClientBuilder;
}
@Override
public boolean autoRefreshToken() {
return false;
}
@Override
public String toString() {
return WxCpGsonBuilder.create().toJson(this);
}
/**
* 一个provider 会有多个suite,需要唯一标识作为前缀
*/
private String keyWithPrefix(String key) {
return keyPrefix + ":" + suiteId + ":" + key;
}
/**
* provider 应该独享一个key,且不和任何suite关联
* 一个provider 会有多个suite,不同的suite 都应该指向同一个provider 的数据
*/
private String providerKeyWithPrefix(String key) {
return keyPrefix + ":" + corpId + ":" + key;
public WxCpTpRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) {
super(new RedissonWxRedisOps(redissonClient), keyPrefix);
}
}

View File

@@ -855,6 +855,10 @@ public interface WxCpApiPathConsts {
* The constant GET_PERMANENT_CODE.
*/
String GET_PERMANENT_CODE = "/cgi-bin/service/get_permanent_code";
/**
* The constant GET_V2_PERMANENT_CODE.
*/
String GET_V2_PERMANENT_CODE = "/cgi-bin/service/v2/get_permanent_code";
/**
* The constant GET_SUITE_TOKEN.
*/

View File

@@ -8,6 +8,7 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.cp.bean.*;
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import java.util.List;
@@ -186,6 +187,8 @@ public interface WxCpTpService {
@Deprecated
WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException;
WxCpTpCorp getV2PermanentCode(String authCode) throws WxErrorException;
/**
* 获取企业永久授权码信息
* <pre>
@@ -200,6 +203,8 @@ public interface WxCpTpService {
*/
WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException;
WxCpTpPermanentCodeInfo getV2PermanentCodeInfo(String authCode) throws WxErrorException;
/**
* <pre>
* 获取预授权链接
@@ -343,9 +348,7 @@ public interface WxCpTpService {
* 获取WxCpTpConfigStorage 对象.
*
* @return WxCpTpConfigStorage wx cp tp config storage
* @deprecated storage应该在service内部使用 ,提供这个接口,容易破坏这个封装
*/
@Deprecated
WxCpTpConfigStorage getWxCpTpConfigStorage();
/**
@@ -527,6 +530,11 @@ public interface WxCpTpService {
*/
WxCpTpLicenseService getWxCpTpLicenseService();
WxCpTpXmlMessage fromEncryptedXml(String encryptedXml,
String timestamp, String nonce, String msgSignature);
String getVerifyDecrypt(String sVerifyEchoStr);
/**
* 获取应用的管理员列表
*

View File

@@ -25,8 +25,10 @@ import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.bean.*;
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.tp.service.*;
import me.chanjar.weixin.cp.util.crypto.WxCpTpCryptUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
@@ -91,6 +93,7 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
private final WxSessionManager sessionManager = new StandardSessionManager();
/**
* 临时文件目录.
*/
@@ -259,6 +262,18 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
return wxCpTpCorp;
}
@Override
public WxCpTpCorp getV2PermanentCode(String authCode) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("auth_code", authCode);
String result = post(configStorage.getApiUrl(GET_V2_PERMANENT_CODE), jsonObject.toString());
jsonObject = GsonParser.parse(result);
WxCpTpCorp wxCpTpCorp = WxCpTpCorp.fromJson(jsonObject.get("auth_corp_info").getAsJsonObject().toString());
wxCpTpCorp.setPermanentCode(jsonObject.get("permanent_code").getAsString());
return wxCpTpCorp;
}
@Override
public WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
@@ -267,6 +282,14 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
return WxCpTpPermanentCodeInfo.fromJson(result);
}
@Override
public WxCpTpPermanentCodeInfo getV2PermanentCodeInfo(String authCode) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("auth_code", authCode);
String result = post(configStorage.getApiUrl(GET_V2_PERMANENT_CODE), jsonObject.toString());
return WxCpTpPermanentCodeInfo.fromJson(result);
}
@Override
@SneakyThrows
public String getPreAuthUrl(String redirectUri, String state) throws WxErrorException {
@@ -452,7 +475,7 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
if (error.getErrorCode() == WxCpErrorMsgEnum.CODE_42009.getCode()) {
// 强制设置wxCpTpConfigStorage它的suite access token过期了这样在下一次请求里就会刷新suite access token
this.configStorage.expireSuiteAccessToken();
if (this.getWxCpTpConfigStorage().autoRefreshToken()) {
if (this.configStorage.autoRefreshToken()) {
log.warn("即将重新获取新的access_token错误代码{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
return this.execute(executor, uri, data);
}
@@ -646,6 +669,27 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
this.wxCpTpUserService = wxCpTpUserService;
}
/**
*
* @param encryptedXml the encrypted xml
* @param timestamp the timestamp
* @param nonce the nonce
* @param msgSignature the msg signature
* @return the wx cp tp xml message
*/
@Override
public WxCpTpXmlMessage fromEncryptedXml(String encryptedXml,
String timestamp, String nonce, String msgSignature) {
return WxCpTpXmlMessage.fromEncryptedXml(encryptedXml,this.configStorage,timestamp,nonce,msgSignature);
}
@Override
public String getVerifyDecrypt(String sVerifyEchoStr) {
WxCpTpCryptUtil cryptUtil = new WxCpTpCryptUtil(this.configStorage);
return cryptUtil.decrypt(sVerifyEchoStr);
}
@Override
public WxCpTpAdmin getAdminList(String authCorpId, Integer agentId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
@@ -764,4 +808,9 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
public void setWxCpTpOAuth2Service(WxCpTpOAuth2Service wxCpTpOAuth2Service) {
this.wxCpTpOAuth2Service = wxCpTpOAuth2Service;
}
@Override
public WxCpTpConfigStorage getWxCpTpConfigStorage() {
return this.configStorage;
}
}

View File

@@ -101,9 +101,9 @@ public class WxCpTpServiceApacheHttpClientImpl extends BaseWxCpTpServiceImpl<Clo
this.httpClient = apacheHttpClientBuilder.build();
}
@Override
public WxCpTpConfigStorage getWxCpTpConfigStorage() {
return this.configStorage;
}
// @Override
// public WxCpTpConfigStorage getWxCpTpConfigStorage() {
// return this.configStorage;
// }
}

View File

@@ -98,9 +98,9 @@ public class WxCpTpServiceHttpComponentsImpl extends BaseWxCpTpServiceImpl<Close
this.httpClient = apacheHttpClientBuilder.build();
}
@Override
public WxCpTpConfigStorage getWxCpTpConfigStorage() {
return this.configStorage;
}
// @Override
// public WxCpTpConfigStorage getWxCpTpConfigStorage() {
// return this.configStorage;
// }
}

View File

@@ -0,0 +1,104 @@
package me.chanjar.weixin.cp.tp.service.impl;
import com.google.gson.JsonObject;
import jodd.http.HttpConnectionProvider;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import jodd.http.net.SocketHttpConnectionProvider;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpClientType;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
/**
* The type Wx cp service jodd http.
*
* @author someone
*/
public class WxCpTpServiceJoddHttpImpl extends BaseWxCpTpServiceImpl<HttpConnectionProvider, ProxyInfo> {
private HttpConnectionProvider httpClient;
private ProxyInfo httpProxy;
@Override
public HttpConnectionProvider getRequestHttpClient() {
return httpClient;
}
@Override
public ProxyInfo getRequestHttpProxy() {
return httpProxy;
}
@Override
public HttpClientType getRequestType() {
return HttpClientType.JODD_HTTP;
}
@Override
public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException {
if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) {
return this.configStorage.getSuiteAccessToken();
}
synchronized (this.globalSuiteAccessTokenRefreshLock) {
// 构建请求 URL
String url = this.configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("suite_id", this.configStorage.getSuiteId());
jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret());
jsonObject.addProperty("suite_ticket", this.getSuiteTicket());
String jsonBody = jsonObject.toString();
if (this.httpProxy != null) {
httpClient.useProxy(this.httpProxy);
}
// 创建 POST 请求
HttpRequest request = HttpRequest
.post(url)
.contentType("application/json")
.body(jsonBody); // 使用 .body() 设置请求体
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);
}
// 更新 access token
jsonObject = GsonParser.parse(resultContent);
String suiteAccussToken = jsonObject.get("suite_access_token").getAsString();
int expiresIn = jsonObject.get("expires_in").getAsInt();
this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn);
}
return this.configStorage.getSuiteAccessToken();
}
@Override
public void initHttp() {
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) {
httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(),
configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword());
}
httpClient = new SocketHttpConnectionProvider();
}
//
// @Override
// public WxCpConfigStorage getWxCpConfigStorage() {
// return this.configStorage;
// }
}

View File

@@ -0,0 +1,130 @@
package me.chanjar.weixin.cp.tp.service.impl;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.http.HttpClientType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import okhttp3.*;
import java.io.IOException;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN;
/**
* The type Wx cp service ok http.
*
* @author someone
*/
@Slf4j
public class WxCpTpServiceOkHttpImpl extends BaseWxCpTpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
private OkHttpClient httpClient;
private OkHttpProxyInfo httpProxy;
@Override
public OkHttpClient getRequestHttpClient() {
return httpClient;
}
@Override
public OkHttpProxyInfo getRequestHttpProxy() {
return httpProxy;
}
@Override
public HttpClientType getRequestType() {
return HttpClientType.OK_HTTP;
}
@Override
public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException {
if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) {
return this.configStorage.getSuiteAccessToken();
}
synchronized (this.globalSuiteAccessTokenRefreshLock) {
// 得到 httpClient
OkHttpClient client = getRequestHttpClient();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("suite_id", this.configStorage.getSuiteId());
jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret());
jsonObject.addProperty("suite_ticket", this.getSuiteTicket());
String jsonBody = jsonObject.toString();
RequestBody requestBody = RequestBody.create(
MediaType.get("application/json; charset=utf-8"),
jsonBody
);
// 构建 POST 请求
Request request = new Request.Builder()
.url(this.configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN)) // URL 不包含查询参数
.post(requestBody) // 使用 POST 方法
.build();
String resultContent = null;
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
resultContent = response.body().string();
} catch (IOException e) {
log.error("获取 suite token 失败: {}", e.getMessage(), e);
throw new WxRuntimeException("获取 suite token 失败", e);
}
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
jsonObject = GsonParser.parse(resultContent);
String suiteAccussToken = jsonObject.get("suite_access_token").getAsString();
int expiresIn = jsonObject.get("expires_in").getAsInt();
this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn);
}
return this.configStorage.getSuiteAccessToken();
}
@Override
public void initHttp() {
log.debug("WxCpServiceOkHttpImpl initHttp");
//设置代理
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(),
configStorage.getHttpProxyPort(),
configStorage.getHttpProxyUsername(),
configStorage.getHttpProxyPassword());
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.proxy(getRequestHttpProxy().getProxy());
//设置授权
clientBuilder.authenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword());
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
});
httpClient = clientBuilder.build();
} else {
httpClient = DefaultOkHttpClientBuilder.get().build();
}
}
// @Override
// public WxCpConfigStorage getWxCpConfigStorage() {
// return this.configStorage;
// }
}

View File

@@ -23,7 +23,7 @@ public class WxCpTpCryptUtil extends WxCryptUtil {
* @param encodingAesKey 公众平台上开发者设置的EncodingAESKey
* @param appidOrCorpid 公众平台corpId
*/
String encodingAesKey = wxCpTpConfigStorage.getAesKey();
String encodingAesKey = wxCpTpConfigStorage.getEncodingAESKey();
String token = wxCpTpConfigStorage.getToken();
String corpId = wxCpTpConfigStorage.getCorpId();

View File

@@ -9,6 +9,8 @@ import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo;
import me.chanjar.weixin.cp.bean.WxTpCustomizedAuthUrl;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.AbstractWxCpTpInRedisConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedisTemplateConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedissonConfigImpl;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import org.mockito.Mockito;
@@ -69,7 +71,10 @@ public class BaseWxCpTpServiceImplTest {
* @return the wx cp tp config storage
*/
public WxCpTpConfigStorage wxCpTpConfigStorage() {
return WxCpTpRedissonConfigImpl.builder().corpId(PROVIDER_CORP_ID).providerSecret(PROVIDER_SECRET).wxRedisOps(new RedissonWxRedisOps(redissonClient())).build();
WxCpTpRedissonConfigImpl wxCpTpRedissonConfig=new WxCpTpRedissonConfigImpl(redissonClient(),"");
wxCpTpRedissonConfig.setCorpId(PROVIDER_CORP_ID);
wxCpTpRedissonConfig.setProviderSecret(PROVIDER_SECRET);
return wxCpTpRedissonConfig;
}
/**

View File

@@ -6,6 +6,8 @@ import me.chanjar.weixin.common.redis.RedissonWxRedisOps;
import me.chanjar.weixin.cp.bean.WxCpProviderToken;
import me.chanjar.weixin.cp.bean.WxCpTpCorpId2OpenCorpId;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.config.impl.AbstractWxCpTpInRedisConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedisTemplateConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedissonConfigImpl;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import org.apache.commons.lang3.StringUtils;
@@ -48,14 +50,10 @@ public class WxCpTpServiceApacheHttpClientImplTest {
* The constant PROVIDER_CORP_ID.
*/
public static final String PROVIDER_CORP_ID = "xxxxxx";
/**
* The constant CORP_SECRET.
*/
public static final String CORP_SECRET = "xxxxxx";
/**
* The constant PROVIDER_SECRET.
*/
public static final String PROVIDER_SECRET = CORP_SECRET;
public static final String PROVIDER_SECRET = "xxxxxx";
/**
* The constant REDIS_ADDR.
*/
@@ -85,9 +83,15 @@ public class WxCpTpServiceApacheHttpClientImplTest {
* @return the wx cp tp config storage
*/
public WxCpTpConfigStorage wxCpTpConfigStorage() {
return WxCpTpRedissonConfigImpl.builder().baseApiUrl(API_URL).suiteId(SUITE_ID).suiteSecret(SUITE_SECRET)
.token(TOKEN).aesKey(AES_KEY).corpId(PROVIDER_CORP_ID).corpSecret(CORP_SECRET).providerSecret(PROVIDER_SECRET)
.wxRedisOps(new RedissonWxRedisOps(redissonClient())).build();
WxCpTpRedissonConfigImpl wxCpTpRedissonConfig=new WxCpTpRedissonConfigImpl(redissonClient(),"");
wxCpTpRedissonConfig.setBaseApiUrl(API_URL);
wxCpTpRedissonConfig.setSuiteId(SUITE_ID);
wxCpTpRedissonConfig.setSuiteSecret(SUITE_SECRET);
wxCpTpRedissonConfig.setToken(TOKEN);
wxCpTpRedissonConfig.setEncodingAESKey(AES_KEY);
wxCpTpRedissonConfig.setCorpId(PROVIDER_CORP_ID);
wxCpTpRedissonConfig.setProviderSecret(PROVIDER_SECRET);
return wxCpTpRedissonConfig;
}
/**