@@ -1,13 +1,21 @@ | |||||
package me.chanjar.weixin.common.bean; | package me.chanjar.weixin.common.bean; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.NoArgsConstructor; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
/** | /** | ||||
* jspai signature. | * jspai signature. | ||||
* | |||||
* @author chanjarster | |||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class WxJsapiSignature implements Serializable { | public class WxJsapiSignature implements Serializable { | ||||
private static final long serialVersionUID = -1116808193154384804L; | private static final long serialVersionUID = -1116808193154384804L; | ||||
@@ -0,0 +1,48 @@ | |||||
package cn.binarywang.wx.miniapp.api; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* jsapi相关接口 | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public interface WxMaJsapiService { | |||||
/** | |||||
* 获得jsapi_ticket的url | |||||
*/ | |||||
String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | |||||
/** | |||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | |||||
* | |||||
* @see #getJsapiTicket(boolean) | |||||
*/ | |||||
String getJsapiTicket() throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获得jsapi_ticket | |||||
* 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | |||||
* | |||||
* 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
* </pre> | |||||
* | |||||
* @param forceRefresh 强制刷新 | |||||
*/ | |||||
String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 创建调用jsapi时所需要的签名 | |||||
* | |||||
* 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
* </pre> | |||||
*/ | |||||
WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||||
} |
@@ -2,6 +2,7 @@ package cn.binarywang.wx.miniapp.api; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
@@ -17,6 +18,7 @@ public interface WxMaService { | |||||
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | ||||
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | ||||
/** | /** | ||||
* 获取登录后的session信息 | * 获取登录后的session信息 | ||||
* | * | ||||
@@ -149,6 +151,13 @@ public interface WxMaService { | |||||
*/ | */ | ||||
WxMaCodeService getCodeService(); | WxMaCodeService getCodeService(); | ||||
/** | |||||
* 返回jsapi操作相关的 API服务类对象 | |||||
* | |||||
* @return WxMaJsapiService | |||||
*/ | |||||
WxMaJsapiService getJsapiService(); | |||||
/** | /** | ||||
* 小程序修改服务器地址、成员管理 API | * 小程序修改服务器地址、成员管理 API | ||||
* | * | ||||
@@ -0,0 +1,75 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | |||||
import cn.binarywang.wx.miniapp.api.WxMaJsapiService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.RandomUtils; | |||||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
import java.util.concurrent.locks.Lock; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxMaJsapiServiceImpl implements WxMaJsapiService { | |||||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
private WxMaService wxMaService; | |||||
public WxMaJsapiServiceImpl(WxMaService wxMaService) { | |||||
this.wxMaService = wxMaService; | |||||
} | |||||
@Override | |||||
public String getJsapiTicket() throws WxErrorException { | |||||
return getJsapiTicket(false); | |||||
} | |||||
@Override | |||||
public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | |||||
Lock lock = this.wxMaService.getWxMaConfig().getJsapiTicketLock(); | |||||
try { | |||||
lock.lock(); | |||||
if (forceRefresh) { | |||||
this.wxMaService.getWxMaConfig().expireJsapiTicket(); | |||||
} | |||||
if (this.wxMaService.getWxMaConfig().isJsapiTicketExpired()) { | |||||
String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL, null); | |||||
JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
this.wxMaService.getWxMaConfig().updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
} | |||||
} finally { | |||||
lock.unlock(); | |||||
} | |||||
return this.wxMaService.getWxMaConfig().getJsapiTicket(); | |||||
} | |||||
@Override | |||||
public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { | |||||
long timestamp = System.currentTimeMillis() / 1000; | |||||
String randomStr = RandomUtils.getRandomStr(); | |||||
String jsapiTicket = getJsapiTicket(false); | |||||
String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, | |||||
"noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url); | |||||
return WxJsapiSignature | |||||
.builder() | |||||
.appId(this.wxMaService.getWxMaConfig().getAppid()) | |||||
.timestamp(timestamp) | |||||
.nonceStr(randomStr) | |||||
.url(url) | |||||
.signature(signature) | |||||
.build(); | |||||
} | |||||
} |
@@ -1,14 +1,6 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaMediaService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaMsgService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaSettingService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||||
import cn.binarywang.wx.miniapp.api.*; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||
@@ -58,6 +50,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
private WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | private WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | ||||
private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | ||||
private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | ||||
private WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | |||||
private int retrySleepMillis = 1000; | private int retrySleepMillis = 1000; | ||||
private int maxRetryTimes = 5; | private int maxRetryTimes = 5; | ||||
@@ -310,6 +303,11 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
return this.codeService; | return this.codeService; | ||||
} | } | ||||
@Override | |||||
public WxMaJsapiService getJsapiService() { | |||||
return this.jsapiService; | |||||
} | |||||
@Override | @Override | ||||
public WxMaSettingService getSettingService() { | public WxMaSettingService getSettingService() { | ||||
return this.settingService; | return this.settingService; | ||||
@@ -38,6 +38,25 @@ public interface WxMaConfig { | |||||
*/ | */ | ||||
void updateAccessToken(String accessToken, int expiresInSeconds); | void updateAccessToken(String accessToken, int expiresInSeconds); | ||||
String getJsapiTicket(); | |||||
Lock getJsapiTicketLock(); | |||||
boolean isJsapiTicketExpired(); | |||||
/** | |||||
* 强制将jsapi ticket过期掉 | |||||
*/ | |||||
void expireJsapiTicket(); | |||||
/** | |||||
* 应该是线程安全的 | |||||
* | |||||
* @param jsapiTicket 新的jsapi ticket值 | |||||
* @param expiresInSeconds 过期时间,以秒为单位 | |||||
*/ | |||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||||
String getAppid(); | String getAppid(); | ||||
String getSecret(); | String getSecret(); | ||||
@@ -27,7 +27,11 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||||
protected volatile String httpProxyUsername; | protected volatile String httpProxyUsername; | ||||
protected volatile String httpProxyPassword; | protected volatile String httpProxyPassword; | ||||
protected volatile String jsapiTicket; | |||||
protected volatile long jsapiTicketExpiresTime; | |||||
protected Lock accessTokenLock = new ReentrantLock(); | protected Lock accessTokenLock = new ReentrantLock(); | ||||
protected Lock jsapiTicketLock = new ReentrantLock(); | |||||
/** | /** | ||||
* 临时文件目录 | * 临时文件目录 | ||||
@@ -70,6 +74,33 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | ||||
} | } | ||||
@Override | |||||
public String getJsapiTicket() { | |||||
return this.jsapiTicket; | |||||
} | |||||
@Override | |||||
public Lock getJsapiTicketLock() { | |||||
return this.jsapiTicketLock; | |||||
} | |||||
@Override | |||||
public boolean isJsapiTicketExpired() { | |||||
return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | |||||
} | |||||
@Override | |||||
public void expireJsapiTicket() { | |||||
this.jsapiTicketExpiresTime = 0; | |||||
} | |||||
@Override | |||||
public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
this.jsapiTicket = jsapiTicket; | |||||
// 预留200秒的时间 | |||||
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
} | |||||
@Override | @Override | ||||
public void expireAccessToken() { | public void expireAccessToken() { | ||||
this.expiresTime = 0; | this.expiresTime = 0; | ||||
@@ -0,0 +1,45 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Test | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxMaJsapiServiceImplTest { | |||||
@Inject | |||||
private WxMaService wxService; | |||||
@Inject | |||||
private WxMaConfig wxMaConfig; | |||||
@Test | |||||
public void testGetJsapiTicket() throws WxErrorException { | |||||
assertThat(this.wxService.getJsapiService().getJsapiTicket()).isNotBlank(); | |||||
} | |||||
@Test | |||||
public void testGetJsapiTicket1() throws WxErrorException { | |||||
assertThat(this.wxService.getJsapiService().getJsapiTicket(true)).isNotBlank(); | |||||
} | |||||
@Test | |||||
public void testCreateJsapiSignature() throws WxErrorException { | |||||
final WxJsapiSignature jsapiSignature = this.wxService.getJsapiService().createJsapiSignature("http://www.qq.com"); | |||||
System.out.println(jsapiSignature); | |||||
assertThat(jsapiSignature).isNotNull(); | |||||
} | |||||
} |
@@ -54,6 +54,7 @@ public class TestConfig extends WxMaInMemoryConfig { | |||||
this.templateId = templateId; | this.templateId = templateId; | ||||
} | } | ||||
@Override | |||||
public void setAccessTokenLock(Lock lock) { | public void setAccessTokenLock(Lock lock) { | ||||
super.accessTokenLock = lock; | super.accessTokenLock = lock; | ||||
} | } | ||||