@@ -1,13 +1,21 @@ | |||
package me.chanjar.weixin.common.bean; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import java.io.Serializable; | |||
/** | |||
* jspai signature. | |||
* | |||
* @author chanjarster | |||
*/ | |||
@Data | |||
@Builder | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class WxJsapiSignature implements Serializable { | |||
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.config.WxMaConfig; | |||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
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 JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | |||
/** | |||
* 获取登录后的session信息 | |||
* | |||
@@ -149,6 +151,13 @@ public interface WxMaService { | |||
*/ | |||
WxMaCodeService getCodeService(); | |||
/** | |||
* 返回jsapi操作相关的 API服务类对象 | |||
* | |||
* @return WxMaJsapiService | |||
*/ | |||
WxMaJsapiService getJsapiService(); | |||
/** | |||
* 小程序修改服务器地址、成员管理 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; | |||
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.config.WxMaConfig; | |||
import com.google.common.base.Joiner; | |||
@@ -58,6 +50,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
private WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | |||
private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | |||
private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | |||
private WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | |||
private int retrySleepMillis = 1000; | |||
private int maxRetryTimes = 5; | |||
@@ -310,6 +303,11 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
return this.codeService; | |||
} | |||
@Override | |||
public WxMaJsapiService getJsapiService() { | |||
return this.jsapiService; | |||
} | |||
@Override | |||
public WxMaSettingService getSettingService() { | |||
return this.settingService; | |||
@@ -38,6 +38,25 @@ public interface WxMaConfig { | |||
*/ | |||
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 getSecret(); | |||
@@ -27,7 +27,11 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||
protected volatile String httpProxyUsername; | |||
protected volatile String httpProxyPassword; | |||
protected volatile String jsapiTicket; | |||
protected volatile long jsapiTicketExpiresTime; | |||
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; | |||
} | |||
@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 | |||
public void expireAccessToken() { | |||
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; | |||
} | |||
@Override | |||
public void setAccessTokenLock(Lock lock) { | |||
super.accessTokenLock = lock; | |||
} | |||