mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-10-21 02:57:37 +08:00
#901 企业微信增加获取用于计算agentConfig签名的应用jsapi_ticket的接口
This commit is contained in:
@@ -17,6 +17,7 @@ import me.chanjar.weixin.cp.config.WxCpConfigStorage;
|
||||
*/
|
||||
public interface WxCpService {
|
||||
String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
|
||||
String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config";
|
||||
String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
|
||||
String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
|
||||
String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty";
|
||||
@@ -75,6 +76,33 @@ public interface WxCpService {
|
||||
*/
|
||||
String getJsapiTicket(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket
|
||||
* 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别:
|
||||
*
|
||||
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
|
||||
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
|
||||
* @see #getJsapiTicket(boolean)
|
||||
*/
|
||||
String getAgentJsapiTicket() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 获取应用的jsapi_ticket
|
||||
* 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别:
|
||||
*
|
||||
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
|
||||
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
|
||||
*
|
||||
* 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
|
||||
*
|
||||
* 详情请见:https://work.weixin.qq.com/api/doc#10029/%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84jsapi_ticket
|
||||
* </pre>
|
||||
*
|
||||
* @param forceRefresh 强制刷新
|
||||
*/
|
||||
String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 创建调用jsapi时所需要的签名
|
||||
|
@@ -1,11 +1,5 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -23,18 +17,15 @@ 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.cp.api.WxCpAgentService;
|
||||
import me.chanjar.weixin.cp.api.WxCpChatService;
|
||||
import me.chanjar.weixin.cp.api.WxCpDepartmentService;
|
||||
import me.chanjar.weixin.cp.api.WxCpMediaService;
|
||||
import me.chanjar.weixin.cp.api.WxCpMenuService;
|
||||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service;
|
||||
import me.chanjar.weixin.cp.api.WxCpService;
|
||||
import me.chanjar.weixin.cp.api.WxCpTagService;
|
||||
import me.chanjar.weixin.cp.api.WxCpUserService;
|
||||
import me.chanjar.weixin.cp.api.*;
|
||||
import me.chanjar.weixin.cp.bean.WxCpMessage;
|
||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
|
||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author chanjarster
|
||||
@@ -61,14 +52,19 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
|
||||
*/
|
||||
protected final Object globalJsapiTicketRefreshLock = new Object();
|
||||
|
||||
/**
|
||||
* 全局的是否正在刷新agent的jsapi_ticket的锁
|
||||
*/
|
||||
protected final Object globalAgentJsapiTicketRefreshLock = new Object();
|
||||
|
||||
protected WxCpConfigStorage configStorage;
|
||||
|
||||
private WxSessionManager sessionManager = new StandardSessionManager();
|
||||
|
||||
protected WxSessionManager sessionManager = new StandardSessionManager();
|
||||
/**
|
||||
* 临时文件目录
|
||||
*/
|
||||
protected File tmpDirFile;
|
||||
private File tmpDirFile;
|
||||
private int retrySleepMillis = 1000;
|
||||
private int maxRetryTimes = 5;
|
||||
|
||||
@@ -88,6 +84,30 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
|
||||
return getAccessToken(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAgentJsapiTicket() throws WxErrorException {
|
||||
return this.getAgentJsapiTicket(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException {
|
||||
if (forceRefresh) {
|
||||
this.configStorage.expireAgentJsapiTicket();
|
||||
}
|
||||
|
||||
if (this.configStorage.isAgentJsapiTicketExpired()) {
|
||||
synchronized (this.globalAgentJsapiTicketRefreshLock) {
|
||||
if (this.configStorage.isAgentJsapiTicketExpired()) {
|
||||
String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null);
|
||||
JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
|
||||
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(),
|
||||
jsonObject.get("expires_in").getAsInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.configStorage.getAgentJsapiTicket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJsapiTicket() throws WxErrorException {
|
||||
@@ -99,19 +119,18 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
|
||||
if (forceRefresh) {
|
||||
this.configStorage.expireJsapiTicket();
|
||||
}
|
||||
|
||||
if (this.configStorage.isJsapiTicketExpired()) {
|
||||
synchronized (this.globalJsapiTicketRefreshLock) {
|
||||
if (this.configStorage.isJsapiTicketExpired()) {
|
||||
String responseContent = execute(SimpleGetRequestExecutor.create(this), WxCpService.GET_JSAPI_TICKET, null);
|
||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
|
||||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
|
||||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
|
||||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
|
||||
this.configStorage.updateJsapiTicket(jsapiTicket,
|
||||
expiresInSeconds);
|
||||
String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null);
|
||||
JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
|
||||
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(),
|
||||
tmpJsonObject.get("expires_in").getAsInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.configStorage.getJsapiTicket();
|
||||
}
|
||||
|
||||
|
@@ -36,11 +36,23 @@ public interface WxCpConfigStorage {
|
||||
|
||||
/**
|
||||
* 应该是线程安全的
|
||||
*
|
||||
* @param jsapiTicket
|
||||
*/
|
||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
|
||||
|
||||
String getAgentJsapiTicket();
|
||||
|
||||
boolean isAgentJsapiTicketExpired();
|
||||
|
||||
/**
|
||||
* 强制将jsapi ticket过期掉
|
||||
*/
|
||||
void expireAgentJsapiTicket();
|
||||
|
||||
/**
|
||||
* 应该是线程安全的
|
||||
*/
|
||||
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds);
|
||||
|
||||
String getCorpId();
|
||||
|
||||
String getCorpSecret();
|
||||
|
@@ -1,11 +1,11 @@
|
||||
package me.chanjar.weixin.cp.config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
|
||||
*
|
||||
@@ -32,6 +32,9 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
|
||||
protected volatile String jsapiTicket;
|
||||
protected volatile long jsapiTicketExpiresTime;
|
||||
|
||||
protected volatile String agentJsapiTicket;
|
||||
protected volatile long agentJsapiTicketExpiresTime;
|
||||
|
||||
protected volatile File tmpDirFile;
|
||||
|
||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
|
||||
@@ -95,6 +98,28 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
|
||||
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAgentJsapiTicket() {
|
||||
return this.agentJsapiTicket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAgentJsapiTicketExpired() {
|
||||
return System.currentTimeMillis() > this.agentJsapiTicketExpiresTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireAgentJsapiTicket() {
|
||||
this.agentJsapiTicketExpiresTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) {
|
||||
this.agentJsapiTicket = jsapiTicket;
|
||||
// 预留200秒的时间
|
||||
this.agentJsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireJsapiTicket() {
|
||||
this.jsapiTicketExpiresTime = 0;
|
||||
|
@@ -26,6 +26,8 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
|
||||
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
|
||||
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
|
||||
private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
|
||||
private static final String AGENT_JSAPI_TICKET_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET";
|
||||
private static final String AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET_EXPIRES_TIME";
|
||||
/**
|
||||
* Redis clients pool
|
||||
*/
|
||||
@@ -46,7 +48,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
|
||||
public WxCpJedisConfigStorage(JedisPool jedisPool) {
|
||||
this.jedisPool = jedisPool;
|
||||
}
|
||||
|
||||
|
||||
public WxCpJedisConfigStorage(String host, int port) {
|
||||
jedisPool = new JedisPool(host, port);
|
||||
}
|
||||
@@ -83,8 +85,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
|
||||
String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
|
||||
|
||||
if (expiresTimeStr != null) {
|
||||
Long expiresTime = Long.parseLong(expiresTimeStr);
|
||||
return System.currentTimeMillis() > expiresTime;
|
||||
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -123,17 +124,15 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
|
||||
|
||||
@Override
|
||||
public boolean isJsapiTicketExpired() {
|
||||
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
|
||||
|
||||
if (expiresTimeStr != null) {
|
||||
Long expiresTime = Long.parseLong(expiresTimeStr);
|
||||
long expiresTime = Long.parseLong(expiresTimeStr);
|
||||
return System.currentTimeMillis() > expiresTime;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,16 +145,51 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
|
||||
|
||||
@Override
|
||||
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
|
||||
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
jedis.set(JS_API_TICKET_KEY, jsapiTicket);
|
||||
|
||||
jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
|
||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAgentJsapiTicket() {
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
return jedis.get(String.format(AGENT_JSAPI_TICKET_KEY, agentId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAgentJsapiTicketExpired() {
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
String expiresTimeStr = jedis.get(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId));
|
||||
|
||||
if (expiresTimeStr != null) {
|
||||
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireAgentJsapiTicket() {
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), "0");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) {
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
jedis.set(String.format(AGENT_JSAPI_TICKET_KEY, agentId), jsapiTicket);
|
||||
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId),
|
||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCorpId() {
|
||||
return this.corpId;
|
||||
|
@@ -0,0 +1,31 @@
|
||||
package me.chanjar.weixin.cp.api.impl;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.cp.api.ApiTestModule;
|
||||
import me.chanjar.weixin.cp.api.WxCpService;
|
||||
import org.testng.annotations.Guice;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Created by BinaryWang on 2019/3/31.
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
@Test
|
||||
@Guice(modules = ApiTestModule.class)
|
||||
public class BaseWxCpServiceImplTest {
|
||||
@Inject
|
||||
protected WxCpService wxService;
|
||||
|
||||
@Test
|
||||
public void testGetAgentJsapiTicket() throws WxErrorException {
|
||||
assertThat(this.wxService.getAgentJsapiTicket()).isNotEmpty();
|
||||
assertThat(this.wxService.getAgentJsapiTicket(true)).isNotEmpty();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user