diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java index cc2333086..c97facd30 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java @@ -10,27 +10,49 @@ import java.util.Arrays; public class SHA1 { /** - * 生成SHA1签名 + * 串接arr参数,生成sha1 digest + * * @param arr * @return */ public static String gen(String... arr) throws NoSuchAlgorithmException { Arrays.sort(arr); StringBuilder sb = new StringBuilder(); - for(String a : arr) { + for (String a : arr) { sb.append(a); } + return genStr(sb.toString()); + } + /** + * 用&串接arr参数,生成sha1 digest + * + * @param arr + * @return + */ + public static String genWithAmple(String... arr) throws NoSuchAlgorithmException { + Arrays.sort(arr); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arr.length; i++) { + String a = arr[i]; + sb.append(a); + if (i != arr.length - 1) { + sb.append('&'); + } + } + return genStr(sb.toString()); + } + + public static String genStr(String str) throws NoSuchAlgorithmException { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); - sha1.update(sb.toString().getBytes()); + sha1.update(str.getBytes()); byte[] output = sha1.digest(); return bytesToHex(output); } - protected static String bytesToHex(byte[] b) { - char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; StringBuffer buf = new StringBuffer(); for (int j = 0; j < b.length; j++) { buf.append(hexDigit[(b[j] >> 4) & 0x0f]); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index 3ecbf155a..68d427851 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -9,10 +9,19 @@ import me.chanjar.weixin.common.bean.WxAccessToken; */ public interface WxMpConfigStorage { + /** + * 应该是线程安全的 + * @param accessToken + */ public void updateAccessToken(WxAccessToken accessToken); - + + /** + * 应该是线程安全的 + * @param accessToken + * @param expiresIn + */ public void updateAccessToken(String accessToken, int expiresIn); - + public String getAccessToken(); public String getAppId(); @@ -35,4 +44,15 @@ public interface WxMpConfigStorage { public String getHttp_proxy_password(); + + public String getJsapiTicket(); + + public boolean isJsapiTokenExpired(); + + /** + * 应该是线程安全的 + * @param jsapiTicket + */ + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index 82c3d6212..d278d1b73 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -9,6 +9,8 @@ import me.chanjar.weixin.common.bean.WxAccessToken; */ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { + private static final long l = 7000 * 1000l; + protected String appId; protected String secret; protected String token; @@ -23,11 +25,14 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected String http_proxy_username; protected String http_proxy_password; - public void updateAccessToken(WxAccessToken accessToken) { + protected String jsapiTicket; + protected long jsapiTicketExpiresTime; + + public synchronized void updateAccessToken(WxAccessToken accessToken) { updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } - public void updateAccessToken(String accessToken, int expiresIn) { + public synchronized void updateAccessToken(String accessToken, int expiresIn) { this.accessToken = accessToken; this.expiresIn = expiresIn; } @@ -121,6 +126,21 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { this.http_proxy_password = http_proxy_password; } + + public String getJsapiTicket() { + return jsapiTicket; + } + + public boolean isJsapiTokenExpired() { + return System.currentTimeMillis() > this.jsapiTicketExpiresTime; + } + + public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + this.jsapiTicket = jsapiTicket; + // 预留200秒的时间 + this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; + } + @Override public String toString() { return "WxMpInMemoryConfigStorage{" + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index c4dc3e541..e8a56787e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -43,17 +43,42 @@ public interface WxMpService { * @throws me.chanjar.weixin.common.exception.WxErrorException */ public void accessTokenRefresh() throws WxErrorException; - + + /** + *
+ * 获得jsapi_ticket + * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 + * + * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 + *+ * @return + * @throws WxErrorException + */ + public String getJsapiTicket() throws WxErrorException; + + /** + *
+ * 创建调用jsapi时所需要的签名 + * + * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 + *+ * @param timestamp 时间戳 + * @param noncestr 用户自己生成的随机字符串 + * @param url url + * @return + */ + public String createJsapiSignature(String timestamp, String noncestr, String url) throws WxErrorException; + /** *
* 上传多媒体文件
- *
+ *
* 上传的多媒体文件有格式和大小限制,如下:
* 图片(image): 1M,支持JPG格式
* 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
* 视频(video):10MB,支持MP4格式
* 缩略图(thumb):64KB,支持JPG格式
- *
+ *
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
*
* @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index dbf2adc40..636e8f622 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -36,6 +36,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
+import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -106,7 +107,38 @@ public class WxMpServiceImpl implements WxMpService {
// 刷新完毕了,就没他什么事儿了
}
}
-
+
+ public String getJsapiTicket() throws WxErrorException {
+ if (wxMpConfigStorage.isJsapiTokenExpired()) {
+ synchronized (wxMpConfigStorage) {
+ if (wxMpConfigStorage.isJsapiTokenExpired()) {
+ String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
+ String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
+ JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
+ JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
+ String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
+ int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
+ wxMpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds);
+ }
+ }
+ }
+ return wxMpConfigStorage.getJsapiTicket();
+ }
+
+ public String createJsapiSignature(String timestamp, String noncestr, String url) throws WxErrorException {
+ String jsapiTicket = getJsapiTicket();
+ try {
+ return SHA1.genWithAmple(
+ "jsapi_ticket=" + jsapiTicket,
+ "timestamp=" + timestamp,
+ "noncestr=" + noncestr,
+ "url=" + url
+ );
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public void customMessageSend(WxMpCustomMessage message) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
execute(new SimplePostRequestExecutor(), url, message.toJson());