diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index c25c053d9..8180d67c9 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -80,6 +80,10 @@
org.projectlombok
lombok
+
+ org.redisson
+ redisson
+
com.github.jedis-lock
jedis-lock
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java
new file mode 100644
index 000000000..ef5facb82
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java
@@ -0,0 +1,153 @@
+package cn.binarywang.wx.miniapp.config.impl;
+
+import lombok.NonNull;
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.redis.RedissonWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RedissonClient;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * 基于Redisson的实现
+ *
+ * @author yuanqixun
+ * @date 2020/5/3
+ */
+public class WxMaRedissonConfigImpl extends WxMaDefaultConfigImpl {
+
+ protected final static String LOCK_KEY = "wechat_ma_lock:";
+ protected final static String MA_ACCESS_TOKEN_KEY = "wechat_ma_access_token_key:";
+ protected final static String MA_JSAPI_TICKET_KEY = "wechat_ma_jsapi_ticket_key:";
+ protected final static String MA_CARD_API_TICKET_KEY = "wechat_ma_card_api_ticket_key:";
+
+ /**
+ * redis 存储的 key 的前缀,可为空
+ */
+ /**
+ * redis 存储的 key 的前缀,可为空
+ */
+ protected String keyPrefix;
+ protected String accessTokenKey;
+ protected String jsapiTicketKey;
+ protected String cardApiTicketKey;
+ protected String lockKey;
+
+ private final WxRedisOps redisOps;
+
+ public WxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) {
+ this(new RedissonWxRedisOps(redissonClient), keyPrefix);
+ }
+
+ public WxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient) {
+ this(redissonClient, null);
+ }
+
+ private WxMaRedissonConfigImpl(@NonNull WxRedisOps redisOps, String keyPrefix) {
+ this.redisOps = redisOps;
+ this.keyPrefix = keyPrefix;
+ }
+
+ @Override
+ public void setAppid(String appid) {
+ super.setAppid(appid);
+ String prefix = StringUtils.isBlank(keyPrefix) ? "" :
+ (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":"));
+ lockKey = prefix + LOCK_KEY.concat(appid);
+ accessTokenKey = prefix + MA_ACCESS_TOKEN_KEY.concat(appid);
+ jsapiTicketKey = prefix + MA_JSAPI_TICKET_KEY.concat(appid);
+ cardApiTicketKey = prefix + MA_CARD_API_TICKET_KEY.concat(appid);
+ }
+
+ protected Lock getLockByKey(String key) {
+ return redisOps.getLock(key);
+ }
+
+ @Override
+ public Lock getAccessTokenLock() {
+ return getLockByKey(this.lockKey.concat(":").concat("accessToken"));
+ }
+
+ @Override
+ public Lock getCardApiTicketLock() {
+ return getLockByKey(this.lockKey.concat(":").concat("cardApiTicket"));
+
+ }
+
+ @Override
+ public Lock getJsapiTicketLock() {
+ return getLockByKey(this.lockKey.concat(":").concat("jsapiTicket"));
+ }
+
+ @Override
+ public String getAccessToken() {
+ return redisOps.getValue(this.accessTokenKey);
+ }
+
+ @Override
+ public boolean isAccessTokenExpired() {
+ Long expire = redisOps.getExpire(this.accessTokenKey);
+ return expire == null || expire < 2;
+ }
+
+ @Override
+ public void updateAccessToken(WxAccessToken accessToken) {
+ redisOps.setValue(this.accessTokenKey, accessToken.getAccessToken(), accessToken.getExpiresIn(), TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void updateAccessToken(String accessToken, int expiresInSeconds) {
+ redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public String getJsapiTicket() {
+ return redisOps.getValue(this.jsapiTicketKey);
+ }
+
+ @Override
+ public boolean isJsapiTicketExpired() {
+ Long expire = redisOps.getExpire(this.jsapiTicketKey);
+ return expire == null || expire < 2;
+ }
+
+ @Override
+ public void expireJsapiTicket() {
+ redisOps.expire(this.jsapiTicketKey, 0, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+ redisOps.setValue(this.jsapiTicketKey, jsapiTicket, expiresInSeconds, TimeUnit.SECONDS);
+
+ }
+
+ @Override
+ public String getCardApiTicket() {
+ return redisOps.getValue(cardApiTicketKey);
+ }
+
+ @Override
+ public boolean isCardApiTicketExpired() {
+ Long expire = redisOps.getExpire(this.cardApiTicketKey);
+ return expire == null || expire < 2;
+ }
+
+ @Override
+ public void expireCardApiTicket() {
+ redisOps.expire(this.cardApiTicketKey, 0, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
+ redisOps.setValue(this.cardApiTicketKey, cardApiTicket, expiresInSeconds, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void expireAccessToken() {
+ redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS);
+ }
+
+}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java
new file mode 100644
index 000000000..d4fb45963
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java
@@ -0,0 +1,72 @@
+package cn.binarywang.wx.miniapp.config.impl;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import lombok.SneakyThrows;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * @author yqx
+ * @date 2020/5/3
+ */
+public class WxMaRedissonConfigImplTest {
+
+ WxMaDefaultConfigImpl wxMaConfig;
+
+ @BeforeMethod
+ public void setUp() {
+ Config config = new Config();
+ config.useSingleServer().setAddress("redis://127.0.0.1:6379")
+ .setDatabase(0);
+ config.setTransportMode(TransportMode.NIO);
+ RedissonClient redisson = Redisson.create(config);
+ wxMaConfig = new WxMaRedissonConfigImpl(redisson);
+ wxMaConfig.setAppid("appid12345678");
+ wxMaConfig.updateAccessToken("accessToken", 5); //有效期5秒
+ wxMaConfig.updateJsapiTicket("jsapiTicket", 5);
+ wxMaConfig.updateCardApiTicket("cardApiTicket", 5);
+ }
+
+ @SneakyThrows
+ @Test
+ public void testGetAccessToken() {
+ String accessToken = wxMaConfig.getAccessToken();
+ Assert.assertEquals(accessToken, "accessToken");
+ Assert.assertFalse(wxMaConfig.isAccessTokenExpired());
+ Thread.sleep(6000);//休眠6s
+ Assert.assertTrue(wxMaConfig.isAccessTokenExpired());
+ }
+
+ @SneakyThrows
+ @Test
+ public void testGetJsapiTicket() {
+ String jsapiTicket = wxMaConfig.getJsapiTicket();
+ Assert.assertEquals(jsapiTicket, "jsapiTicket");
+ Assert.assertFalse(wxMaConfig.isJsapiTicketExpired());
+ Thread.sleep(6000);//休眠6s
+ Assert.assertTrue(wxMaConfig.isJsapiTicketExpired());
+ }
+
+ @SneakyThrows
+ @Test
+ public void testGetCardApiTicket() {
+ String cardApiTicket = wxMaConfig.getCardApiTicket();
+ Assert.assertEquals(cardApiTicket, "cardApiTicket");
+ Assert.assertFalse(wxMaConfig.isCardApiTicketExpired());
+ Thread.sleep(6000);//休眠6s
+ Assert.assertTrue(wxMaConfig.isCardApiTicketExpired());
+ }
+
+ @Test
+ public void testIsAccessTokenExpired() {
+ Assert.assertFalse(wxMaConfig.isAccessTokenExpired());
+ wxMaConfig.expireAccessToken();
+ Assert.assertTrue(wxMaConfig.isAccessTokenExpired());
+ }
+}