mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-03-10 00:13:40 +08:00
🎨 #1646 企业微信第三方应用(服务商)模块重构实现,并提供Router、Interceptor、Handler等接口
This commit is contained in:
@@ -1,229 +0,0 @@
|
||||
package me.chanjar.weixin.cp.api;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
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.WxCpMaJsCode2SessionResult;
|
||||
import me.chanjar.weixin.cp.bean.WxCpTpAuthInfo;
|
||||
import me.chanjar.weixin.cp.bean.WxCpTpCorp;
|
||||
import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo;
|
||||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
|
||||
|
||||
/**
|
||||
* 微信第三方应用API的Service.
|
||||
*
|
||||
* @author zhenjun cai
|
||||
*/
|
||||
public interface WxCpTpService {
|
||||
/**
|
||||
* <pre>
|
||||
* 验证推送过来的消息的正确性
|
||||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90139/90968/消息体签名校验
|
||||
* </pre>
|
||||
*
|
||||
* @param msgSignature 消息签名
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机数
|
||||
* @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息
|
||||
* @return the boolean
|
||||
*/
|
||||
boolean checkSignature(String msgSignature, String timestamp, String nonce, String data);
|
||||
|
||||
/**
|
||||
* 获取suite_access_token, 不强制刷新suite_access_token
|
||||
*
|
||||
* @return the suite access token
|
||||
* @throws WxErrorException the wx error exception
|
||||
* @see #getSuiteAccessToken(boolean) #getSuiteAccessToken(boolean)
|
||||
*/
|
||||
String getSuiteAccessToken() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获取suite_access_token,本方法线程安全
|
||||
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
|
||||
* 另:本service的所有方法都会在suite_access_token过期是调用此方法
|
||||
* 程序员在非必要情况下尽量不要主动调用此方法
|
||||
* 详情请见: https://work.weixin.qq.com/api/doc#90001/90143/90600
|
||||
* </pre>
|
||||
*
|
||||
* @param forceRefresh 强制刷新
|
||||
* @return the suite access token
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获得suite_ticket,不强制刷新suite_ticket
|
||||
*
|
||||
* @return the suite ticket
|
||||
* @throws WxErrorException the wx error exception
|
||||
* @see #getSuiteTicket(boolean) #getSuiteTicket(boolean)
|
||||
*/
|
||||
String getSuiteTicket() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获得suite_ticket
|
||||
* 由于suite_ticket是微信服务器定时推送(每10分钟),不能主动获取,如果碰到过期只能抛异常
|
||||
*
|
||||
* 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
|
||||
* </pre>
|
||||
*
|
||||
* @param forceRefresh 强制刷新
|
||||
* @return the suite ticket
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String getSuiteTicket(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 小程序登录凭证校验
|
||||
*
|
||||
* @param jsCode 登录时获取的 code
|
||||
* @return the wx cp ma js code 2 session result
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取企业凭证
|
||||
*
|
||||
* @param authCorpid 授权方corpid
|
||||
* @param permanentCode 永久授权码,通过get_permanent_code获取
|
||||
* @return the corp token
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取企业永久授权码 .
|
||||
*
|
||||
* @param authCode .
|
||||
* @return . permanent code
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
@Deprecated
|
||||
WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取企业永久授权码信息
|
||||
* <pre>
|
||||
* 原来的方法实现不全
|
||||
* </pre>
|
||||
*
|
||||
* @param authCode the auth code
|
||||
* @return permanent code info
|
||||
* @throws WxErrorException the wx error exception
|
||||
* @author yuan
|
||||
* @since 2020 -03-18
|
||||
*/
|
||||
WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获取预授权链接
|
||||
* </pre>
|
||||
*
|
||||
* @param redirectUri 授权完成后的回调网址
|
||||
* @param state a-zA-Z0-9的参数值(不超过128个字节),用于第三方自行校验session,防止跨域攻击
|
||||
* @return pre auth url
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String getPreAuthUrl(String redirectUri, String state) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取企业的授权信息
|
||||
*
|
||||
* @param authCorpId 授权企业的corpId
|
||||
* @param permanentCode 授权企业的永久授权码
|
||||
* @return auth info
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
WxCpTpAuthInfo getAuthInfo(String authCorpId, String permanentCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
|
||||
*
|
||||
* @param url 接口地址
|
||||
* @param queryParam 请求参数
|
||||
* @return the string
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String get(String url, String queryParam) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
|
||||
*
|
||||
* @param url 接口地址
|
||||
* @param postData 请求body字符串
|
||||
* @return the string
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
String post(String url, String postData) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Service没有实现某个API的时候,可以用这个,
|
||||
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
|
||||
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> 请求值类型
|
||||
* @param <E> 返回值类型
|
||||
* @param executor 执行器
|
||||
* @param uri 请求地址
|
||||
* @param data 参数
|
||||
* @return the t
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
|
||||
* 默认:1000ms
|
||||
* </pre>
|
||||
*
|
||||
* @param retrySleepMillis 重试休息时间
|
||||
*/
|
||||
void setRetrySleepMillis(int retrySleepMillis);
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 设置当微信系统响应系统繁忙时,最大重试次数.
|
||||
* 默认:5次
|
||||
* </pre>
|
||||
*
|
||||
* @param maxRetryTimes 最大重试次数
|
||||
*/
|
||||
void setMaxRetryTimes(int maxRetryTimes);
|
||||
|
||||
/**
|
||||
* 初始化http请求对象
|
||||
*/
|
||||
void initHttp();
|
||||
|
||||
/**
|
||||
* 获取WxMpConfigStorage 对象.
|
||||
*
|
||||
* @return WxMpConfigStorage wx cp tp config storage
|
||||
*/
|
||||
WxCpTpConfigStorage getWxCpTpConfigStorage();
|
||||
|
||||
/**
|
||||
* 注入 {@link WxCpTpConfigStorage} 的实现.
|
||||
*
|
||||
* @param wxConfigProvider 配置对象
|
||||
*/
|
||||
void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider);
|
||||
|
||||
/**
|
||||
* http请求对象.
|
||||
*
|
||||
* @return the request http
|
||||
*/
|
||||
RequestHttp<?, ?> getRequestHttp();
|
||||
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.SneakyThrows;
|
||||
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.WxCpErrorMsgEnum;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.DataUtils;
|
||||
import me.chanjar.weixin.common.util.crypto.SHA1;
|
||||
import me.chanjar.weixin.common.util.http.RequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.json.GsonParser;
|
||||
import me.chanjar.weixin.cp.api.WxCpTpService;
|
||||
import me.chanjar.weixin.cp.bean.*;
|
||||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.*;
|
||||
|
||||
/**
|
||||
* .
|
||||
*
|
||||
* @author zhenjun cai
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, RequestHttp<H, P> {
|
||||
|
||||
/**
|
||||
* 全局的是否正在刷新access token的锁.
|
||||
*/
|
||||
protected final Object globalSuiteAccessTokenRefreshLock = new Object();
|
||||
|
||||
/**
|
||||
* 全局的是否正在刷新jsapi_ticket的锁.
|
||||
*/
|
||||
protected final Object globalSuiteTicketRefreshLock = new Object();
|
||||
|
||||
protected WxCpTpConfigStorage configStorage;
|
||||
|
||||
/**
|
||||
* 临时文件目录.
|
||||
*/
|
||||
private File tmpDirFile;
|
||||
private int retrySleepMillis = 1000;
|
||||
private int maxRetryTimes = 5;
|
||||
|
||||
@Override
|
||||
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) {
|
||||
try {
|
||||
return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data)
|
||||
.equals(msgSignature);
|
||||
} catch (Exception e) {
|
||||
log.error("Checking signature failed, and the reason is :" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteAccessToken() throws WxErrorException {
|
||||
return getSuiteAccessToken(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteTicket() throws WxErrorException {
|
||||
return getSuiteTicket(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteTicket(boolean forceRefresh) throws WxErrorException {
|
||||
// suite ticket由微信服务器推送,不能强制刷新
|
||||
// if (forceRefresh) {
|
||||
// this.configStorage.expireSuiteTicket();
|
||||
// }
|
||||
|
||||
if (this.configStorage.isSuiteTicketExpired()) {
|
||||
// 本地suite ticket 不存在或者过期
|
||||
WxError wxError = WxError.fromJson("{\"errcode\":40085, \"errmsg\":\"invaild suite ticket\"}", WxType.CP);
|
||||
throw new WxErrorException(wxError);
|
||||
}
|
||||
return this.configStorage.getSuiteTicket();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException {
|
||||
Map<String, String> params = new HashMap<>(2);
|
||||
params.put("js_code", jsCode);
|
||||
params.put("grant_type", "authorization_code");
|
||||
|
||||
final String url = configStorage.getApiUrl(JSCODE_TO_SESSION);
|
||||
return WxCpMaJsCode2SessionResult.fromJson(this.get(url, Joiner.on("&").withKeyValueSeparator("=").join(params)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("auth_corpid", authCorpid);
|
||||
jsonObject.addProperty("permanent_code", permanentCode);
|
||||
String result = post(configStorage.getApiUrl(GET_CORP_TOKEN), jsonObject.toString());
|
||||
|
||||
return WxAccessToken.fromJson(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("auth_code", authCode);
|
||||
|
||||
String result = post(configStorage.getApiUrl(GET_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();
|
||||
jsonObject.addProperty("auth_code", authCode);
|
||||
String result = post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString());
|
||||
return WxCpTpPermanentCodeInfo.fromJson(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String getPreAuthUrl(String redirectUri, String state) throws WxErrorException {
|
||||
String result = get(configStorage.getApiUrl(GET_PREAUTH_CODE), null);
|
||||
WxCpTpPreauthCode preAuthCode = WxCpTpPreauthCode.fromJson(result);
|
||||
String preAuthUrl = "https://open.work.weixin.qq.com/3rdapp/install?suite_id=" + configStorage.getSuiteId() +
|
||||
"&pre_auth_code=" + preAuthCode.getPreAuthCode() + "&redirect_uri=" + URLEncoder.encode(redirectUri, "utf-8");
|
||||
if (StringUtils.isNotBlank(state)) {
|
||||
preAuthUrl += "&state=" + state;
|
||||
}
|
||||
return preAuthUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpTpAuthInfo getAuthInfo(String authCorpId, String permanentCode) throws WxErrorException {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("auth_corpid", authCorpId);
|
||||
jsonObject.addProperty("permanent_code", permanentCode);
|
||||
String result = post(configStorage.getApiUrl(GET_AUTH_INFO), jsonObject.toString());
|
||||
return WxCpTpAuthInfo.fromJson(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String url, String queryParam) throws WxErrorException {
|
||||
return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String post(String url, String postData) throws WxErrorException {
|
||||
return execute(SimplePostRequestExecutor.create(this), url, postData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求.
|
||||
*/
|
||||
@Override
|
||||
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
int retryTimes = 0;
|
||||
do {
|
||||
try {
|
||||
return this.executeInternal(executor, uri, data);
|
||||
} catch (WxErrorException e) {
|
||||
if (retryTimes + 1 > this.maxRetryTimes) {
|
||||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
|
||||
//最后一次重试失败后,直接抛出异常,不再等待
|
||||
throw new RuntimeException("微信服务端异常,超出重试次数");
|
||||
}
|
||||
|
||||
WxError error = e.getError();
|
||||
/*
|
||||
* -1 系统繁忙, 1000ms后重试
|
||||
*/
|
||||
if (error.getErrorCode() == -1) {
|
||||
int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
|
||||
try {
|
||||
log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
|
||||
Thread.sleep(sleepMillis);
|
||||
} catch (InterruptedException e1) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} while (retryTimes++ < this.maxRetryTimes);
|
||||
|
||||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
|
||||
throw new RuntimeException("微信服务端异常,超出重试次数");
|
||||
}
|
||||
|
||||
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
E dataForLog = DataUtils.handleDataWithSecret(data);
|
||||
|
||||
if (uri.contains("suite_access_token=")) {
|
||||
throw new IllegalArgumentException("uri参数中不允许有suite_access_token: " + uri);
|
||||
}
|
||||
String suiteAccessToken = getSuiteAccessToken(false);
|
||||
|
||||
String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "suite_access_token=" + suiteAccessToken;
|
||||
|
||||
try {
|
||||
T result = executor.execute(uriWithAccessToken, data, WxType.CP);
|
||||
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result);
|
||||
return result;
|
||||
} catch (WxErrorException e) {
|
||||
WxError error = e.getError();
|
||||
/*
|
||||
* 发生以下情况时尝试刷新suite_access_token
|
||||
* 42009 suite_access_token已过期
|
||||
*/
|
||||
if (error.getErrorCode() == WxCpErrorMsgEnum.CODE_42009.getCode()) {
|
||||
// 强制设置wxCpTpConfigStorage它的suite access token过期了,这样在下一次请求里就会刷新suite access token
|
||||
this.configStorage.expireSuiteAccessToken();
|
||||
if (this.getWxCpTpConfigStorage().autoRefreshToken()) {
|
||||
log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg());
|
||||
return this.execute(executor, uri, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.getErrorCode() != 0) {
|
||||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
|
||||
throw new WxErrorException(error, e);
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider) {
|
||||
this.configStorage = wxConfigProvider;
|
||||
this.initHttp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRetrySleepMillis(int retrySleepMillis) {
|
||||
this.retrySleepMillis = retrySleepMillis;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setMaxRetryTimes(int maxRetryTimes) {
|
||||
this.maxRetryTimes = maxRetryTimes;
|
||||
}
|
||||
|
||||
public File getTmpDirFile() {
|
||||
return this.tmpDirFile;
|
||||
}
|
||||
|
||||
public void setTmpDirFile(File tmpDirFile) {
|
||||
this.tmpDirFile = tmpDirFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestHttp<?, ?> getRequestHttp() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package me.chanjar.weixin.cp.api.impl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.cp.api.WxCpTpService;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
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.HttpType;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.common.util.json.GsonParser;
|
||||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
|
||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author someone
|
||||
*/
|
||||
public class WxCpTpServiceApacheHttpClientImpl extends BaseWxCpTpServiceImpl<CloseableHttpClient, HttpHost> {
|
||||
private CloseableHttpClient httpClient;
|
||||
private HttpHost httpProxy;
|
||||
|
||||
@Override
|
||||
public CloseableHttpClient getRequestHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHost getRequestHttpProxy() {
|
||||
return httpProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpType getRequestType() {
|
||||
return HttpType.APACHE_HTTP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) {
|
||||
return this.configStorage.getSuiteAccessToken();
|
||||
}
|
||||
|
||||
synchronized (this.globalSuiteAccessTokenRefreshLock) {
|
||||
try {
|
||||
HttpPost httpPost = new HttpPost(configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN));
|
||||
if (this.httpProxy != null) {
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setProxy(this.httpProxy).build();
|
||||
httpPost.setConfig(config);
|
||||
}
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("suite_id", this.configStorage.getSuiteId());
|
||||
jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret());
|
||||
jsonObject.addProperty("suite_ticket", this.getSuiteTicket());
|
||||
StringEntity entity = new StringEntity(jsonObject.toString(), Consts.UTF_8);
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
String resultContent;
|
||||
try (CloseableHttpClient httpclient = getRequestHttpClient();
|
||||
CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
||||
resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
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();
|
||||
Integer expiresIn = jsonObject.get("expires_in").getAsInt();
|
||||
this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this.configStorage.getSuiteAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initHttp() {
|
||||
ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage.getApacheHttpClientBuilder();
|
||||
if (null == apacheHttpClientBuilder) {
|
||||
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
|
||||
}
|
||||
|
||||
apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost())
|
||||
.httpProxyPort(this.configStorage.getHttpProxyPort())
|
||||
.httpProxyUsername(this.configStorage.getHttpProxyUsername())
|
||||
.httpProxyPassword(this.configStorage.getHttpProxyPassword());
|
||||
|
||||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) {
|
||||
this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort());
|
||||
}
|
||||
|
||||
this.httpClient = apacheHttpClientBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpTpConfigStorage getWxCpTpConfigStorage() {
|
||||
return this.configStorage;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 默认接口实现类,使用apache httpclient实现
|
||||
* Created by zhenjun cai.
|
||||
* </pre>
|
||||
*
|
||||
* @author zhenjun cai
|
||||
*/
|
||||
public class WxCpTpServiceImpl extends WxCpTpServiceApacheHttpClientImpl {
|
||||
}
|
||||
Reference in New Issue
Block a user