From d52a55ff8dacafbf384f93cc6aa98d6bd0dc1784 Mon Sep 17 00:00:00 2001
From: 007gzs <007gzs@gmail.com>
Date: Thu, 9 Nov 2017 11:46:07 +0800
Subject: [PATCH] =?UTF-8?q?#=20368=20=E5=A2=9E=E5=8A=A0=20=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E7=AC=AC=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0=20?=
=?UTF-8?q?=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 增加 微信第三方平台 接口
* WxOpenXmlMessage 消息处理
---
pom.xml | 768 +++++++++---------
weixin-java-open/README.md | 62 ++
weixin-java-open/pom.xml | 97 +++
.../open/api/WxOpenComponentService.java | 82 ++
.../weixin/open/api/WxOpenConfigStorage.java | 106 +++
.../weixin/open/api/WxOpenService.java | 22 +
.../api/impl/WxOpenComponentServiceImpl.java | 218 +++++
.../api/impl/WxOpenInMemoryConfigStorage.java | 386 +++++++++
.../api/impl/WxOpenInRedisConfigStorage.java | 164 ++++
.../open/api/impl/WxOpenMpServiceImpl.java | 49 ++
.../api/impl/WxOpenServiceAbstractImpl.java | 63 ++
.../WxOpenServiceApacheHttpClientImpl.java | 44 +
.../open/api/impl/WxOpenServiceImpl.java | 8 +
.../bean/WxOpenAuthorizerAccessToken.java | 35 +
.../open/bean/WxOpenComponentAccessToken.java | 35 +
.../bean/auth/WxOpenAuthorizationInfo.java | 18 +
.../open/bean/auth/WxOpenAuthorizerInfo.java | 22 +
.../open/bean/message/WxOpenXmlMessage.java | 100 +++
.../result/WxOpenAuthorizerInfoResult.java | 16 +
.../result/WxOpenAuthorizerOptionResult.java | 15 +
.../bean/result/WxOpenQueryAuthResult.java | 14 +
.../weixin/open/util/WxOpenCryptUtil.java | 28 +
.../WxOpenAuthorizationInfoGsonAdapter.java | 45 +
...xOpenAuthorizerAccessTokenGsonAdapter.java | 21 +
.../json/WxOpenAuthorizerInfoGsonAdapter.java | 38 +
...WxOpenAuthorizerInfoResultGsonAdapter.java | 32 +
...OpenAuthorizerOptionResultGsonAdapter.java | 22 +
...WxOpenComponentAccessTokenGsonAdapter.java | 21 +
.../open/util/json/WxOpenGsonBuilder.java | 36 +
.../WxOpenQueryAuthResultGsonAdapter.java | 26 +
.../open/util/xml/XStreamTransformer.java | 90 ++
31 files changed, 2302 insertions(+), 381 deletions(-)
create mode 100644 weixin-java-open/README.md
create mode 100644 weixin-java-open/pom.xml
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoResultGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerOptionResultGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenComponentAccessTokenGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenQueryAuthResultGsonAdapter.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/xml/XStreamTransformer.java
diff --git a/pom.xml b/pom.xml
index 7eb78c33..38d24fd7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,381 +1,387 @@
-
-
- 4.0.0
- com.github.binarywang
- weixin-java-parent
- 2.8.6.BETA
- pom
- WeiXin Java Tools - Parent
- 微信公众号、企业号上级POM
- https://github.com/wechat-group/weixin-java-tools
-
-
-
- The Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
-
-
-
-
-
- Daniel Qian
- chanjarster@gmail.com
- https://github.com/chanjarster
-
-
- Binary Wang
- binarywang@gmail.com
- https://github.com/binarywang
-
-
- gaigeshen
- gaigeshen@qq.com
- https://github.com/gaigeshen
-
-
- Liu Mingbo
- liumingbo2008@gmail.com
- https://github.com/FirenzesEagle
-
-
- kakotor
- kakotor@gmail.com
- https://github.com/kakotor
-
-
- xiong
- zhaoxiong.tan@gmail.com
- https://github.com/ZhaoxiongTan
-
-
- LiuJunGuang
- aimilin@yeah.net
- https://github.com/aimilin6688
-
-
- Eric.Tsai
- xiaodong.cai.ks@gmail.com
- https://github.com/iwareserictsai
-
-
- withinthefog
- withinthefog@gmail.com
- https://github.com/withinthefog
-
-
- Keung
- dongfuqiang1988@163.com
- https://github.com/johnnytung
-
-
- Jonk
- aimilin@yeah.net
- https://github.com/aimilin6688
-
-
- ecoolper
- crskyp@gmail.com
- https://github.com/crskyp
-
-
-
-
- scm:git:https://github.com/wechat-group/weixin-java-tools.git
- scm:git:git@github.com:wechat-group/weixin-java-tools.git
- https://github.com/wechat-group/weixin-java-tools
-
-
-
- weixin-java-common
- weixin-java-cp
- weixin-java-mp
- weixin-java-pay
- weixin-java-miniapp
-
-
-
-
- 1.7
- 1.7
-
- UTF-8
- true
- true
- 4.5
- 9.3.0.RC0
-
-
-
-
-
- com.github.binarywang
- qrcode-utils
- 1.1
-
-
-
- org.jodd
- jodd-http
- 3.7.1
- provided
-
-
- com.squareup.okhttp3
- okhttp
- 3.7.0
- provided
-
-
-
- org.apache.httpcomponents
- httpclient
- ${httpclient.version}
-
-
- org.apache.httpcomponents
- httpmime
- ${httpclient.version}
-
-
- commons-codec
- commons-codec
- 1.10
-
-
- commons-io
- commons-io
- 2.5
-
-
- org.apache.commons
- commons-lang3
- 3.5
-
-
- org.slf4j
- slf4j-api
- 1.7.24
-
-
- com.thoughtworks.xstream
- xstream
- 1.4.9
-
-
-
- com.google.guava
- guava
- 20.0
-
-
- com.google.code.gson
- gson
- 2.8.0
-
-
-
-
- joda-time
- joda-time
- 2.9.7
- test
-
-
- ch.qos.logback
- logback-classic
- 1.1.11
- test
-
-
- com.google.inject
- guice
- 3.0
- test
-
-
- org.testng
- testng
- 6.10
- test
-
-
- org.mockito
- mockito-all
- 1.9.5
- test
-
-
- org.eclipse.jetty
- jetty-server
- ${jetty.version}
- test
-
-
- org.eclipse.jetty
- jetty-servlet
- ${jetty.version}
- test
-
-
- redis.clients
- jedis
- 2.9.0
- provided
-
-
- org.projectlombok
- lombok
- 1.16.18
- compile
-
-
-
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
-
- doclint-java8-disable
-
- [1.8,)
-
-
- -Xdoclint:none
-
-
-
-
- release
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.9.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
- ${javadoc.opts}
- UTF-8
- zh_CN
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.17
-
- true
-
-
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.3
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.5.1
-
- true
- false
- release
- deploy
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.6.0
-
- UTF-8
-
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- 2.17
-
- quality-checks/google_checks.xml
- true
- true
- true
-
-
-
- verify
-
- check
-
-
-
-
-
-
-
-
+
+
+ 4.0.0
+ com.github.binarywang
+ weixin-java-parent
+ 2.8.6.BETA
+ pom
+ WeiXin Java Tools - Parent
+ 微信公众号、企业号上级POM
+ https://github.com/wechat-group/weixin-java-tools
+
+
+
+ The Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+
+
+ Daniel Qian
+ chanjarster@gmail.com
+ https://github.com/chanjarster
+
+
+ Binary Wang
+ binarywang@gmail.com
+ https://github.com/binarywang
+
+
+ gaigeshen
+ gaigeshen@qq.com
+ https://github.com/gaigeshen
+
+
+ Liu Mingbo
+ liumingbo2008@gmail.com
+ https://github.com/FirenzesEagle
+
+
+ kakotor
+ kakotor@gmail.com
+ https://github.com/kakotor
+
+
+ xiong
+ zhaoxiong.tan@gmail.com
+ https://github.com/ZhaoxiongTan
+
+
+ LiuJunGuang
+ aimilin@yeah.net
+ https://github.com/aimilin6688
+
+
+ Eric.Tsai
+ xiaodong.cai.ks@gmail.com
+ https://github.com/iwareserictsai
+
+
+ withinthefog
+ withinthefog@gmail.com
+ https://github.com/withinthefog
+
+
+ Keung
+ dongfuqiang1988@163.com
+ https://github.com/johnnytung
+
+
+ Jonk
+ aimilin@yeah.net
+ https://github.com/aimilin6688
+
+
+ ecoolper
+ crskyp@gmail.com
+ https://github.com/crskyp
+
+
+ 007
+ 007gzs@gmail.com
+ https://github.com/007gzs
+
+
+
+
+ scm:git:https://github.com/wechat-group/weixin-java-tools.git
+ scm:git:git@github.com:wechat-group/weixin-java-tools.git
+ https://github.com/wechat-group/weixin-java-tools
+
+
+
+ weixin-java-common
+ weixin-java-cp
+ weixin-java-mp
+ weixin-java-pay
+ weixin-java-miniapp
+ weixin-java-open
+
+
+
+
+ 1.7
+ 1.7
+
+ UTF-8
+ true
+ true
+ 4.5
+ 9.3.0.RC0
+
+
+
+
+
+ com.github.binarywang
+ qrcode-utils
+ 1.1
+
+
+
+ org.jodd
+ jodd-http
+ 3.7.1
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ 3.7.0
+ provided
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+
+
+ org.apache.httpcomponents
+ httpmime
+ ${httpclient.version}
+
+
+ commons-codec
+ commons-codec
+ 1.10
+
+
+ commons-io
+ commons-io
+ 2.5
+
+
+ org.apache.commons
+ commons-lang3
+ 3.5
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.24
+
+
+ com.thoughtworks.xstream
+ xstream
+ 1.4.9
+
+
+
+ com.google.guava
+ guava
+ 20.0
+
+
+ com.google.code.gson
+ gson
+ 2.8.0
+
+
+
+
+ joda-time
+ joda-time
+ 2.9.7
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ 1.1.11
+ test
+
+
+ com.google.inject
+ guice
+ 3.0
+ test
+
+
+ org.testng
+ testng
+ 6.10
+ test
+
+
+ org.mockito
+ mockito-all
+ 1.9.5
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ ${jetty.version}
+ test
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${jetty.version}
+ test
+
+
+ redis.clients
+ jedis
+ 2.9.0
+ provided
+
+
+ org.projectlombok
+ lombok
+ 1.16.18
+ compile
+
+
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+ doclint-java8-disable
+
+ [1.8,)
+
+
+ -Xdoclint:none
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+ ${javadoc.opts}
+ UTF-8
+ zh_CN
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.17
+
+ true
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.3
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.1
+
+ true
+ false
+ release
+ deploy
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.0
+
+ UTF-8
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 2.17
+
+ quality-checks/google_checks.xml
+ true
+ true
+ true
+
+
+
+ verify
+
+ check
+
+
+
+
+
+
+
+
diff --git a/weixin-java-open/README.md b/weixin-java-open/README.md
new file mode 100644
index 00000000..d99b7706
--- /dev/null
+++ b/weixin-java-open/README.md
@@ -0,0 +1,62 @@
+消息机制未实现,下面为通知回调中设置的代码部分
+```
+@RestController
+@RequestMapping("notify")
+public class NotifyController extends WechatThridBaseController {
+ @Autowired
+ protected WxOpenServiceDemo wxOpenService;
+ @RequestMapping("receive_ticket")
+ public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp,
+ @RequestParam("nonce") String nonce, @RequestParam("signature") String signature,
+ @RequestParam(name = "encrypt_type", required = false) String encType,
+ @RequestParam(name = "msg_signature", required = false) String msgSignature) {
+ this.logger.info(
+ "\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}],"
+ + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+ signature, encType, msgSignature, timestamp, nonce, requestBody);
+
+ if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
+ throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+ }
+
+ // aes加密的消息
+ WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
+ this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
+ String out = null;
+ try {
+ out = wxOpenService.getWxOpenComponentService().route(inMessage);
+ } catch (WxErrorException e) {
+ throw new ResponseException(ErrorCodeEnum.ERROR, e);
+ }
+
+ this.logger.debug("\n组装回复信息:{}", out);
+
+ return out;
+ }
+ @RequestMapping("{appId}/callback")
+ public Object callback(@RequestBody(required = false)String requestBody,
+ @PathVariable ("appId") String appId,
+ @RequestParam("signature") String signature,
+ @RequestParam("timestamp") String timestamp,
+ @RequestParam("nonce") String nonce,
+ @RequestParam("openid") String openid,
+ @RequestParam("encrypt_type") String encType,
+ @RequestParam("msg_signature") String msgSignature) {
+ this.logger.info(
+ "\n接收微信请求:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
+ + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+ appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
+ logger.info("query:"+getHttpServletRequest().getQueryString()+"\nbody:"+requestBody);
+ if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
+ throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+ }
+
+ String out = "";
+ // aes加密的消息
+ WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
+ this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
+ //wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId);
+ return out;
+ }
+}
+```
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
new file mode 100644
index 00000000..d451dce1
--- /dev/null
+++ b/weixin-java-open/pom.xml
@@ -0,0 +1,97 @@
+
+
+
+ 4.0.0
+
+ com.github.binarywang
+ weixin-java-parent
+ 2.8.6.BETA
+
+ weixin-java-open
+ WeiXin Java Tools - Open
+ 微信开放平台Java SDK
+
+
+ 007
+ 007gzs@gmail.com
+ https://github.com/007gzs
+
+
+
+
+ com.github.binarywang
+ weixin-java-common
+ ${project.version}
+
+
+ com.github.binarywang
+ weixin-java-mp
+ ${project.version}
+
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+ org.testng
+ testng
+ test
+
+
+ com.google.inject
+ guice
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ test
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ test
+
+
+ joda-time
+ joda-time
+ test
+
+
+ redis.clients
+ jedis
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ src/test/resources/testng.xml
+
+
+
+
+
+
+
+
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
new file mode 100644
index 00000000..ded17248
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
@@ -0,0 +1,82 @@
+package me.chanjar.weixin.open.api;
+
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
+import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
+import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
+import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
+
+/**
+ * @author 007
+ */
+public interface WxOpenComponentService {
+
+ String API_COMPONENT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
+ String API_CREATE_PREAUTHCODE_URL = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode";
+ String API_QUERY_AUTH_URL = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth";
+ String API_AUTHORIZER_TOKEN_URL = "https://api.weixin.qq.com /cgi-bin/component/api_authorizer_token";
+ String API_GET_AUTHORIZER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info";
+ String API_GET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option";
+ String API_SET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/ api_set_authorizer_option";
+
+
+ String COMPONENT_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s";
+ String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect";
+
+ /**
+ * 用code换取oauth2的access token
+ */
+ String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&code=%s&grant_type=authorization_code&component_appid=%s";
+ /**
+ * 刷新oauth2的access token
+ */
+ String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s&component_appid==%s";
+ WxMpService getWxMpServiceByAppid(String appid);
+
+ WxOpenConfigStorage getWxOpenConfigStorage();
+
+ boolean checkSignature(String timestamp, String nonce, String signature);
+
+ String getComponentAccessToken(boolean forceRefresh) throws WxErrorException;
+
+ /**
+ * 获取用户授权页URL(来路URL和成功跳转URL 的域名都需要为三方平台设置的 登录授权的发起页域名)
+ */
+ String getPreAuthUrl(String redirectURI) throws WxErrorException;
+
+ String route(WxOpenXmlMessage wxMessage) throws WxErrorException;
+
+ /**
+ * 使用授权码换取公众号或小程序的接口调用凭据和授权信息
+ */
+ WxOpenQueryAuthResult getQueryAuth(String authorizationCode) throws WxErrorException;
+
+ /**
+ * 获取授权方的帐号基本信息
+ */
+ WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException;
+
+ /**
+ * 获取授权方的选项设置信息
+ */
+ WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException;
+
+ /**
+ * 设置授权方的选项信息
+ */
+ WxError setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException;
+
+ String getAuthorizerAccessToken(String appid, boolean forceRefresh) throws WxErrorException;
+
+ WxMpOAuth2AccessToken oauth2getAccessToken(String appid, String code) throws WxErrorException;
+
+ boolean checkSignature(String appId, String timestamp, String nonce, String signature);
+
+ WxMpOAuth2AccessToken oauth2refreshAccessToken(String appid, String refreshToken) throws WxErrorException;
+
+ String oauth2buildAuthorizationUrl(String appid, String redirectURI, String scope, String state);
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java
new file mode 100644
index 00000000..8f6173b7
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java
@@ -0,0 +1,106 @@
+package me.chanjar.weixin.open.api;
+
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
+import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
+
+/**
+ * @author 007
+ */
+public interface WxOpenConfigStorage {
+
+ void setComponentAppId(String componentAppId);
+
+ void setComponentAppSecret(String componentAppSecret);
+
+ void setComponentToken(String componentToken);
+
+ void setComponentAesKey(String componentAesKey);
+
+ String getComponentAppId();
+ String getComponentAppSecret();
+ String getComponentToken();
+ String getComponentAesKey();
+ String getComponentVerifyTicket();
+ void setComponentVerifyTicket(String componentVerifyTicket);
+ String getComponentAccessToken();
+ boolean isComponentAccessTokenExpired();
+ void updateComponentAccessTokent(WxOpenComponentAccessToken componentAccessToken);
+ WxMpConfigStorage getWxMpConfigStorage(String appId);
+ /**
+ * 应该是线程安全的
+ *
+ * @param componentAccessToken 新的accessToken值
+ * @param expiresInSeconds 过期时间,以秒为单位
+ */
+ void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds);
+
+ /**
+ * 是否自动刷新token
+ */
+ boolean autoRefreshToken();
+
+
+ String getAuthorizerRefreshToken(String appId);
+ void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken);
+ String getAuthorizerAccessToken(String appId);
+
+
+ boolean isAuthorizerAccessTokenExpired(String appId);
+
+ /**
+ * 强制将access token过期掉
+ */
+ void expireAuthorizerAccessToken(String appId);
+
+ /**
+ * 应该是线程安全的
+ *
+ * @param authorizerAccessToken 要更新的WxAccessToken对象
+ */
+ void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken);
+
+ /**
+ * 应该是线程安全的
+ *
+ * @param authorizerAccessToken 新的accessToken值
+ * @param expiresInSeconds 过期时间,以秒为单位
+ */
+ void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds);
+
+ String getJsapiTicket(String appId);
+
+ boolean isJsapiTicketExpired(String appId);
+
+ /**
+ * 强制将jsapi ticket过期掉
+ */
+ void expireJsapiTicket(String appId);
+
+ /**
+ * 应该是线程安全的
+ *
+ * @param jsapiTicket 新的jsapi ticket值
+ * @param expiresInSeconds 过期时间,以秒为单位
+ */
+ void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds);
+
+ String getCardApiTicket(String appId);
+
+
+ boolean isCardApiTicketExpired(String appId);
+
+ /**
+ * 强制将卡券api ticket过期掉
+ */
+ void expireCardApiTicket(String appId);
+
+ /**
+ * 应该是线程安全的
+ *
+ * @param cardApiTicket 新的cardApi ticket值
+ * @param expiresInSeconds 过期时间,以秒为单位
+ */
+ void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds);
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java
new file mode 100644
index 00000000..c0214d46
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java
@@ -0,0 +1,22 @@
+package me.chanjar.weixin.open.api;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+
+/**
+ * @author 007
+ */
+public interface WxOpenService {
+ WxOpenComponentService getWxOpenComponentService();
+ WxOpenConfigStorage getWxOpenConfigStorage();
+ void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage);
+ /**
+ * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求
+ */
+ String get(String url, String queryParam) throws WxErrorException;
+
+ /**
+ * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求
+ */
+ String post(String url, String postData) throws WxErrorException;
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
new file mode 100644
index 00000000..9f797e2f
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
@@ -0,0 +1,218 @@
+package me.chanjar.weixin.open.api.impl;
+
+import com.google.gson.JsonObject;
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.crypto.SHA1;
+import me.chanjar.weixin.common.util.http.URIUtil;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenService;
+import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
+import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
+import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
+import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
+import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
+import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
+import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @author 007
+ */
+public class WxOpenComponentServiceImpl implements WxOpenComponentService {
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+ private WxOpenService wxOpenService;
+ private static final Map wxOpenMpServiceMap = new Hashtable<>();
+
+ public WxOpenComponentServiceImpl(WxOpenService wxOpenService) {
+ this.wxOpenService = wxOpenService;
+ }
+
+ @Override
+ public WxMpService getWxMpServiceByAppid(String appId) {
+ WxMpService wxMpService = wxOpenMpServiceMap.get(appId);
+ if (wxMpService == null) {
+ synchronized (wxOpenMpServiceMap) {
+ wxMpService = wxOpenMpServiceMap.get(appId);
+ if (wxMpService == null) {
+ wxMpService = new WxOpenMpServiceImpl(this, appId, getWxOpenConfigStorage().getWxMpConfigStorage(appId));
+
+ wxOpenMpServiceMap.put(appId, wxMpService);
+ }
+ }
+ }
+ return wxMpService;
+ }
+
+ public WxOpenService getWxOpenService() {
+ return wxOpenService;
+ }
+
+ @Override
+ public WxOpenConfigStorage getWxOpenConfigStorage() {
+ return wxOpenService.getWxOpenConfigStorage();
+ }
+ @Override
+ public boolean checkSignature(String timestamp, String nonce, String signature) {
+ try {
+ return SHA1.gen(getWxOpenConfigStorage().getComponentToken(), timestamp, nonce)
+ .equals(signature);
+ } catch (Exception e) {
+ this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
+ return false;
+ }
+ }
+ @Override
+ public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException {
+
+ if (this.getWxOpenConfigStorage().isComponentAccessTokenExpired() || forceRefresh) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret());
+ jsonObject.addProperty("component_verify_ticket", getWxOpenConfigStorage().getComponentVerifyTicket());
+
+ String responseContent = this.getWxOpenService().post(API_COMPONENT_TOKEN_URL, jsonObject.toString());
+ WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent);
+ getWxOpenConfigStorage().updateComponentAccessTokent(componentAccessToken);
+ }
+ return this.getWxOpenConfigStorage().getComponentAccessToken();
+ }
+
+ private String post(String uri, String postData) throws WxErrorException {
+ String componentAccessToken = getComponentAccessToken(false);
+ String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + "component_access_token=" + componentAccessToken;
+ return getWxOpenService().post(uriWithComponentAccessToken, postData);
+ }
+
+ private String get(String uri) throws WxErrorException {
+ String componentAccessToken = getComponentAccessToken(false);
+ String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + "component_access_token=" + componentAccessToken;
+ return getWxOpenService().get(uriWithComponentAccessToken, null);
+ }
+
+ @Override
+ public String getPreAuthUrl(String redirectURI) throws WxErrorException {
+
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ String responseContent = post(API_CREATE_PREAUTHCODE_URL, jsonObject.toString());
+ jsonObject = WxGsonBuilder.create().fromJson(responseContent, JsonObject.class);
+ return String.format(COMPONENT_LOGIN_PAGE_URL, getWxOpenConfigStorage().getComponentAppId(), jsonObject.get("pre_auth_code").getAsString(), URIUtil.encodeURIComponent(redirectURI));
+ }
+
+ @Override
+ public String route(final WxOpenXmlMessage wxMessage) throws WxErrorException {
+ if (wxMessage == null) {
+ throw new NullPointerException("message is empty");
+ }
+ if (StringUtils.equalsIgnoreCase(wxMessage.getInfoType(), "component_verify_ticket")) {
+ getWxOpenConfigStorage().setComponentVerifyTicket(wxMessage.getComponentVerifyTicket());
+ return "success";
+ }
+ //新增、跟新授权
+ if (StringUtils.equalsAnyIgnoreCase(wxMessage.getInfoType(), "authorized", "updateauthorized")) {
+ WxOpenQueryAuthResult queryAuth = wxOpenService.getWxOpenComponentService().getQueryAuth(wxMessage.getAuthorizationCode());
+ if (queryAuth == null || queryAuth.getAuthorizationInfo() == null || queryAuth.getAuthorizationInfo().getAuthorizerAppid() == null) {
+ throw new NullPointerException("getQueryAuth");
+ }
+ WxOpenAuthorizationInfo authorizationInfo = queryAuth.getAuthorizationInfo();
+ if (authorizationInfo.getAuthorizerAccessToken() != null) {
+ getWxOpenConfigStorage().updateAuthorizerAccessToken(authorizationInfo.getAuthorizerAppid(),
+ authorizationInfo.getAuthorizerAccessToken(), authorizationInfo.getExpiresIn());
+ }
+ if (authorizationInfo.getAuthorizerRefreshToken() != null) {
+ getWxOpenConfigStorage().setAuthorizerRefreshToken(authorizationInfo.getAuthorizerAppid(), authorizationInfo.getAuthorizerRefreshToken());
+ }
+ return "success";
+ }
+ return null;
+ }
+ @Override
+ public WxOpenQueryAuthResult getQueryAuth(String authorizationCode) throws WxErrorException {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("authorization_code", authorizationCode);
+ String responseContent = post(API_QUERY_AUTH_URL, jsonObject.toString());
+ return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenQueryAuthResult.class);
+ }
+ @Override
+ public WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("authorizer_appid", authorizerAppid);
+ String responseContent = post(API_GET_AUTHORIZER_INFO_URL, jsonObject.toString());
+ return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerInfoResult.class);
+ }
+
+ @Override
+ public WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("authorizer_appid", authorizerAppid);
+ jsonObject.addProperty("option_name", optionName);
+ String responseContent = post(API_GET_AUTHORIZER_OPTION_URL, jsonObject.toString());
+ return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerOptionResult.class);
+ }
+ @Override
+ public WxError setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("authorizer_appid", authorizerAppid);
+ jsonObject.addProperty("option_name", optionName);
+ jsonObject.addProperty("option_value", optionValue);
+ String responseContent = post(API_SET_AUTHORIZER_OPTION_URL, jsonObject.toString());
+ return WxGsonBuilder.create().fromJson(responseContent, WxError.class);
+ }
+
+ @Override
+ public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws WxErrorException {
+
+ if (this.getWxOpenConfigStorage().isAuthorizerAccessTokenExpired(appId) || forceRefresh) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
+ jsonObject.addProperty("authorizer_appid", appId);
+ jsonObject.addProperty("authorizer_refresh_token", getWxOpenConfigStorage().getAuthorizerRefreshToken(appId));
+ String responseContent = post(API_AUTHORIZER_TOKEN_URL, jsonObject.toString());
+
+ WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent);
+ getWxOpenConfigStorage().updateAuthorizerAccessToken(appId, wxOpenAuthorizerAccessToken);
+ }
+ return this.getWxOpenConfigStorage().getAuthorizerAccessToken(appId);
+ }
+
+ @Override
+ public WxMpOAuth2AccessToken oauth2getAccessToken(String appId, String code) throws WxErrorException {
+ String url = String.format(OAUTH2_ACCESS_TOKEN_URL, appId, code, getWxOpenConfigStorage().getComponentAppId());
+ String responseContent = get(url);
+ return WxMpOAuth2AccessToken.fromJson(responseContent);
+ }
+
+ @Override
+ public boolean checkSignature(String appid, String timestamp, String nonce, String signature) {
+ return false;
+ }
+
+ @Override
+ public WxMpOAuth2AccessToken oauth2refreshAccessToken(String appId, String refreshToken) throws WxErrorException {
+ String url = String.format(OAUTH2_REFRESH_TOKEN_URL, appId, refreshToken, getWxOpenConfigStorage().getComponentAppId());
+ String responseContent = get(url);
+ return WxMpOAuth2AccessToken.fromJson(responseContent);
+ }
+
+ @Override
+ public String oauth2buildAuthorizationUrl(String appId, String redirectURI, String scope, String state) {
+ return String.format(CONNECT_OAUTH2_AUTHORIZE_URL,
+ appId, URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state), getWxOpenConfigStorage().getComponentAppId());
+ }
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
new file mode 100644
index 00000000..dc10ee18
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
@@ -0,0 +1,386 @@
+package me.chanjar.weixin.open.api.impl;
+
+
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.util.ToStringUtils;
+import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
+import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
+ *
+ * @author 007
+ */
+public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
+ private String componentAppId;
+ private String componentAppSecret;
+ private String componentToken;
+ private String componentAesKey;
+ private String componentVerifyTicket;
+ private String componentAccessToken;
+ private long componentExpiresTime;
+
+ private Map authorizerRefreshTokens = new Hashtable<>();
+ private Map authorizerAccessTokens = new Hashtable<>();
+ private Map jsapiTickets = new Hashtable<>();
+ private Map cardApiTickets = new Hashtable<>();
+
+ @Override
+ public void setComponentAppId(String componentAppId) {
+ this.componentAppId = componentAppId;
+ }
+ @Override
+ public void setComponentAppSecret(String componentAppSecret) {
+ this.componentAppSecret = componentAppSecret;
+ }
+ @Override
+ public void setComponentToken(String componentToken) {
+ this.componentToken = componentToken;
+ }
+ @Override
+ public void setComponentAesKey(String componentAesKey) {
+ this.componentAesKey = componentAesKey;
+ }
+
+ @Override
+ public String getComponentAppId() {
+ return componentAppId;
+ }
+
+ @Override
+ public String getComponentAppSecret() {
+ return componentAppSecret;
+ }
+
+ @Override
+ public String getComponentToken() {
+ return componentToken;
+ }
+
+ @Override
+ public String getComponentAesKey() {
+ return componentAesKey;
+ }
+
+ @Override
+ public String getComponentVerifyTicket() {
+ return componentVerifyTicket;
+ }
+
+ @Override
+ public void setComponentVerifyTicket(String componentVerifyTicket) {
+ this.componentVerifyTicket = componentVerifyTicket;
+ }
+
+ @Override
+ public String getComponentAccessToken() {
+ return componentAccessToken;
+ }
+
+ @Override
+ public boolean isComponentAccessTokenExpired() {
+ return System.currentTimeMillis() > componentExpiresTime;
+ }
+
+ @Override
+ public void updateComponentAccessTokent(WxOpenComponentAccessToken componentAccessToken) {
+ updateComponentAccessTokent(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn());
+ }
+
+ @Override
+ public WxMpConfigStorage getWxMpConfigStorage(String appId) {
+ return new WxOpenMpConfigStorage(this, appId);
+ }
+
+ @Override
+ public void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds) {
+ this.componentAccessToken = componentAccessToken;
+ this.componentExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+ }
+
+ @Override
+ public boolean autoRefreshToken() {
+ return true;
+ }
+ private String getTokenString(Map map, String key){
+ Token token = map.get(key);
+ if(token == null || (token.expiresTime != null && System.currentTimeMillis() > token.expiresTime)){
+ return null;
+ }
+ return token.token;
+ }
+ private void expireToken(Map map, String key){
+ Token token = map.get(key);
+ if(token != null){
+ token.expiresTime = 0L;
+ }
+ }
+ private void updateToken(Map map, String key, String tokenString, Integer expiresInSeconds){
+ Token token = map.get(key);
+ if(token == null){
+ token = new Token();
+ map.put(key, token);
+ }
+ token.token = tokenString;
+ if(expiresInSeconds != null) {
+ token.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+ }
+ }
+ @Override
+ public String getAuthorizerRefreshToken(String appId) {
+ return getTokenString(authorizerRefreshTokens, appId);
+ }
+
+ @Override
+ public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) {
+ updateToken(authorizerRefreshTokens, appId, authorizerRefreshToken, null);
+ }
+
+ @Override
+ public String getAuthorizerAccessToken(String appId) {
+ return getTokenString(authorizerAccessTokens, appId);
+ }
+
+
+ @Override
+ public boolean isAuthorizerAccessTokenExpired(String appId) {
+ return getTokenString(authorizerAccessTokens, appId) == null;
+ }
+
+ @Override
+ public void expireAuthorizerAccessToken(String appId) {
+ expireToken(authorizerAccessTokens, appId);
+ }
+
+ @Override
+ public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
+ updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), authorizerAccessToken.getExpiresIn());
+ }
+
+ @Override
+ public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) {
+ updateToken(authorizerAccessTokens, appId, authorizerAccessToken, expiresInSeconds);
+ }
+
+ @Override
+ public String getJsapiTicket(String appId) {
+ return getTokenString(jsapiTickets, appId);
+ }
+
+ @Override
+ public boolean isJsapiTicketExpired(String appId) {
+ return getTokenString(jsapiTickets, appId) == null;
+ }
+
+ @Override
+ public void expireJsapiTicket(String appId) {
+ expireToken(jsapiTickets, appId);
+ }
+
+ @Override
+ public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) {
+ updateToken(jsapiTickets, appId, jsapiTicket, expiresInSeconds);
+ }
+
+ @Override
+ public String getCardApiTicket(String appId) {
+ return getTokenString(cardApiTickets, appId);
+ }
+
+ @Override
+ public boolean isCardApiTicketExpired(String appId) {
+ return getTokenString(cardApiTickets, appId) == null;
+ }
+
+ @Override
+ public void expireCardApiTicket(String appId) {
+ expireToken(cardApiTickets, appId);
+ }
+
+ @Override
+ public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) {
+ updateToken(cardApiTickets, appId, cardApiTicket, expiresInSeconds);
+ }
+
+ private static class Token{
+ private String token;
+ private Long expiresTime;
+ }
+ private static class WxOpenMpConfigStorage implements WxMpConfigStorage{
+ private WxOpenConfigStorage wxOpenConfigStorage;
+ private String appId;
+ private WxOpenMpConfigStorage(WxOpenConfigStorage wxOpenConfigStorage, String appId){
+ this.wxOpenConfigStorage = wxOpenConfigStorage;
+ this.appId = appId;
+ }
+
+ private Lock accessTokenLock = new ReentrantLock();
+ private Lock jsapiTicketLock = new ReentrantLock();
+ private Lock cardApiTicketLock = new ReentrantLock();
+
+
+ @Override
+ public String getAccessToken() {
+ return wxOpenConfigStorage.getAuthorizerAccessToken(appId);
+ }
+
+ @Override
+ public Lock getAccessTokenLock() {
+ return this.accessTokenLock;
+ }
+
+ @Override
+ public boolean isAccessTokenExpired() {
+ return wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appId);
+ }
+
+ @Override
+ public synchronized void updateAccessToken(WxAccessToken accessToken) {
+ updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+ }
+
+ @Override
+ public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+ wxOpenConfigStorage.updateAuthorizerAccessToken(appId, accessToken, expiresInSeconds);
+ }
+
+ @Override
+ public void expireAccessToken() {
+ wxOpenConfigStorage.expireAuthorizerAccessToken(appId);
+ }
+
+ @Override
+ public String getJsapiTicket() {
+ return wxOpenConfigStorage.getJsapiTicket(appId);
+ }
+
+ @Override
+ public Lock getJsapiTicketLock() {
+ return this.jsapiTicketLock;
+ }
+
+ @Override
+ public boolean isJsapiTicketExpired() {
+ return wxOpenConfigStorage.isJsapiTicketExpired(appId);
+ }
+
+ @Override
+ public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+ wxOpenConfigStorage.updateJsapiTicket(appId, jsapiTicket, expiresInSeconds);
+ }
+
+ @Override
+ public void expireJsapiTicket() {
+ wxOpenConfigStorage.expireJsapiTicket(appId);
+ }
+
+ /**
+ * 卡券api_ticket
+ */
+ @Override
+ public String getCardApiTicket() {
+ return wxOpenConfigStorage.getCardApiTicket(appId);
+ }
+
+ @Override
+ public Lock getCardApiTicketLock() {
+ return this.cardApiTicketLock;
+ }
+
+ @Override
+ public boolean isCardApiTicketExpired() {
+ return wxOpenConfigStorage.isCardApiTicketExpired(appId);
+ }
+
+ @Override
+ public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
+ wxOpenConfigStorage.updateCardApiTicket(appId, cardApiTicket, expiresInSeconds);
+ }
+
+ @Override
+ public void expireCardApiTicket() {
+ wxOpenConfigStorage.expireCardApiTicket(appId);
+ }
+
+ @Override
+ public String getAppId() {
+ return this.appId;
+ }
+
+ @Override
+ public String getSecret() {
+ return null;
+ }
+
+ @Override
+ public String getToken() {
+ return null;
+ }
+
+ @Override
+ public long getExpiresTime() {
+ return 0;
+ }
+
+
+ @Override
+ public String getAesKey() {
+ return null;
+ }
+
+ @Override
+ public String getOauth2redirectUri() {
+ return null;
+ }
+
+ @Override
+ public String getHttpProxyHost() {
+ return null;
+ }
+
+ @Override
+ public int getHttpProxyPort() {
+ return 0;
+ }
+
+ @Override
+ public String getHttpProxyUsername() {
+ return null;
+ }
+
+ @Override
+ public String getHttpProxyPassword() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringUtils.toSimpleString(this);
+ }
+
+ @Override
+ public File getTmpDirFile() {
+ return null;
+ }
+
+ @Override
+ public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
+ return null;
+ }
+
+
+ @Override
+ public boolean autoRefreshToken() {
+ return true;
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java
new file mode 100644
index 00000000..faaa95d8
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java
@@ -0,0 +1,164 @@
+package me.chanjar.weixin.open.api.impl;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+/**
+ * @author 007
+ */
+public class WxOpenInRedisConfigStorage extends WxOpenInMemoryConfigStorage {
+
+ private final static String COMPONENT_VERIFY_TICKET_KEY = "wechat_component_verify_ticket:";
+ private final static String COMPONENT_ACCESS_TOKEN_KEY = "wechat_component_access_token:";
+
+ private final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:";
+ private final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:";
+ private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:";
+ private final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:";
+
+
+ protected final JedisPool jedisPool;
+ private String componentVerifyTicketKey;
+ private String componentAccessTokenKey;
+ private String authorizerRefreshTokenKey;
+ private String authorizerAccessTokenKey;
+ private String jsapiTicketKey;
+ private String cardApiTicket;
+ public WxOpenInRedisConfigStorage(JedisPool jedisPool) {
+ this.jedisPool = jedisPool;
+ }
+
+ @Override
+ public void setComponentAppId(String componentAppId) {
+ super.setComponentAppId(componentAppId);
+ componentVerifyTicketKey = COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId);
+ componentAccessTokenKey = COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId);
+ authorizerRefreshTokenKey = AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId);
+ authorizerAccessTokenKey = AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId);
+ jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId);
+ cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId);
+ }
+ @Override
+ public String getComponentVerifyTicket(){
+ try(Jedis jedis = jedisPool.getResource()){
+ return jedis.get(componentVerifyTicketKey);
+ }
+ }
+ @Override
+ public void setComponentVerifyTicket(String componentVerifyTicket){
+ try(Jedis jedis = jedisPool.getResource()){
+ jedis.set(componentVerifyTicketKey, componentVerifyTicket);
+ }
+ }
+ @Override
+ public String getComponentAccessToken(){
+ try(Jedis jedis = jedisPool.getResource()){
+ return jedis.get(componentAccessTokenKey);
+ }
+ }
+ @Override
+ public boolean isComponentAccessTokenExpired(){
+ try(Jedis jedis = jedisPool.getResource()){
+ return jedis.ttl(componentAccessTokenKey) < 2;
+ }
+ }
+ @Override
+ public void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.setex(componentAccessTokenKey, expiresInSeconds - 200, componentAccessToken);
+ }
+ }
+ private String getKey(String prefix, String appId){
+ return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId);
+ }
+
+ @Override
+ public String getAuthorizerRefreshToken(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.get(getKey(authorizerRefreshTokenKey, appId));
+ }
+ }
+ @Override
+ public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.set(getKey(authorizerRefreshTokenKey, appId), authorizerRefreshToken);
+ }
+ }
+ @Override
+ public String getAuthorizerAccessToken(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.get(getKey(authorizerAccessTokenKey, appId));
+ }
+ }
+
+
+ @Override
+ public boolean isAuthorizerAccessTokenExpired(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.ttl(getKey(authorizerAccessTokenKey, appId)) < 2;
+ }
+ }
+ @Override
+ public void expireAuthorizerAccessToken(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.expire(getKey(authorizerAccessTokenKey, appId), 0);
+ }
+ }
+ @Override
+ public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.setex(getKey(authorizerAccessTokenKey, appId), expiresInSeconds - 200, authorizerAccessToken);
+ }
+ }
+
+ @Override
+ public String getJsapiTicket(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.get(getKey(jsapiTicketKey, appId));
+ }
+ }
+
+ @Override
+ public boolean isJsapiTicketExpired(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.ttl(getKey(jsapiTicketKey, appId)) < 2;
+ }
+ }
+ @Override
+ public void expireJsapiTicket(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.expire(getKey(jsapiTicketKey, appId), 0);
+ }
+ }
+ @Override
+ public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.setex(getKey(jsapiTicketKey, appId), expiresInSeconds - 200, jsapiTicket);
+ }
+ }
+
+ @Override
+ public String getCardApiTicket(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.get(getKey(jsapiTicketKey, appId));
+ }
+ }
+ @Override
+ public boolean isCardApiTicketExpired(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ return jedis.ttl(getKey(cardApiTicket, appId)) < 2;
+ }
+ }
+ @Override
+ public void expireCardApiTicket(String appId){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.expire(getKey(cardApiTicket, appId), 0);
+ }
+ }
+ @Override
+ public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds){
+ try (Jedis jedis = this.jedisPool.getResource()) {
+ jedis.setex(getKey(cardApiTicket, appId), expiresInSeconds - 200, cardApiTicket);
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java
new file mode 100644
index 00000000..b4baf585
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java
@@ -0,0 +1,49 @@
+package me.chanjar.weixin.open.api.impl;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+
+/**
+ * @author 007
+ */
+public class WxOpenMpServiceImpl extends WxMpServiceImpl {
+ private WxOpenComponentService wxOpenComponentService;
+ private WxMpConfigStorage wxMpConfigStorage;
+ private String appId;
+ public WxOpenMpServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMpConfigStorage wxMpConfigStorage){
+ this.wxOpenComponentService = wxOpenComponentService;
+ this.appId = appId;
+ this.wxMpConfigStorage = wxMpConfigStorage;
+ }
+ @Override
+ public WxMpConfigStorage getWxMpConfigStorage(){
+ return wxMpConfigStorage;
+ }
+ @Override
+ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+ return wxOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh);
+ }
+
+ @Override
+ public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException {
+ return wxOpenComponentService.oauth2getAccessToken(appId, code);
+ }
+
+// @Override
+// public boolean checkSignature(String timestamp, String nonce, String signature) {
+// return wxOpenComponentService.checkSignature(appId, timestamp, nonce, signature);
+// }
+
+ @Override
+ public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException {
+ return wxOpenComponentService.oauth2refreshAccessToken(appId, refreshToken);
+ }
+
+ @Override
+ public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) {
+ return wxOpenComponentService.oauth2buildAuthorizationUrl(appId, redirectURI, scope, state);
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
new file mode 100644
index 00000000..5f937dc5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
@@ -0,0 +1,63 @@
+package me.chanjar.weixin.open.api.impl;
+
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * @author 007
+ */
+public abstract class WxOpenServiceAbstractImpl implements WxOpenService, RequestHttp {
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+ private WxOpenConfigStorage wxOpenConfigStorage;
+ protected WxOpenComponentService wxOpenComponentService = new WxOpenComponentServiceImpl(this);
+ @Override
+ public WxOpenComponentService getWxOpenComponentService() {
+ return wxOpenComponentService;
+ }
+ @Override
+ public WxOpenConfigStorage getWxOpenConfigStorage(){
+ return wxOpenConfigStorage;
+ }
+ public void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage){
+ this.wxOpenConfigStorage = wxOpenConfigStorage;
+ }
+
+ protected synchronized T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
+ try {
+ T result = executor.execute(uri, data);
+ this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result);
+ return result;
+ } catch (WxErrorException e) {
+ WxError error = e.getError();
+// /*
+// * 发生以下情况时尝试刷新access_token
+// * 40001 获取access_token时AppSecret错误,或者access_token无效
+// * 42001 access_token超时
+// * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口
+// */
+// if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
+// // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
+// this.configStorage.expireAccessToken();
+// return execute(executor, uri, data);
+// }
+
+ if (error.getErrorCode() != 0) {
+ this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, data, error);
+ throw new WxErrorException(error, e);
+ }
+ return null;
+ } catch (IOException e) {
+ this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, data, e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java
new file mode 100644
index 00000000..e5b62b34
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.open.api.impl;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.HttpType;
+import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
+import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
+import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
+import org.apache.http.HttpHost;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+/**
+ * apache-http方式实现
+ * @author 007
+ */
+public class WxOpenServiceApacheHttpClientImpl extends WxOpenServiceAbstractImpl {
+ private CloseableHttpClient httpClient = DefaultApacheHttpClientBuilder.get().build();
+ private HttpHost httpProxy = null;
+
+ @Override
+ public CloseableHttpClient getRequestHttpClient() {
+ return httpClient;
+ }
+
+ @Override
+ public HttpHost getRequestHttpProxy() {
+ return httpProxy;
+ }
+
+ @Override
+ public HttpType getRequestType() {
+ return HttpType.APACHE_HTTP;
+ }
+
+ @Override
+ public String get(String url, String queryParam) throws WxErrorException {
+ return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
+ }
+
+ @Override
+ public String post(String url, String postData) throws WxErrorException {
+ return execute(SimplePostRequestExecutor.create(this), url, postData);
+ }
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java
new file mode 100644
index 00000000..c807ccdf
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java
@@ -0,0 +1,8 @@
+package me.chanjar.weixin.open.api.impl;
+
+/**
+ * @author 007
+ */
+public class WxOpenServiceImpl extends WxOpenServiceApacheHttpClientImpl {
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java
new file mode 100644
index 00000000..429aad17
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java
@@ -0,0 +1,35 @@
+package me.chanjar.weixin.open.bean;
+
+import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+public class WxOpenAuthorizerAccessToken implements Serializable{
+
+ private String authorizerAccessToken;
+
+ private int expiresIn = -1;
+
+ public static WxOpenAuthorizerAccessToken fromJson(String json) {
+ return WxOpenGsonBuilder.create().fromJson(json, WxOpenAuthorizerAccessToken.class);
+ }
+
+ public String getAuthorizerAccessToken() {
+ return authorizerAccessToken;
+ }
+
+ public void setAuthorizerAccessToken(String authorizerAccessToken) {
+ this.authorizerAccessToken = authorizerAccessToken;
+ }
+
+ public int getExpiresIn() {
+ return expiresIn;
+ }
+
+ public void setExpiresIn(int expiresIn) {
+ this.expiresIn = expiresIn;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java
new file mode 100644
index 00000000..31f79b75
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java
@@ -0,0 +1,35 @@
+package me.chanjar.weixin.open.bean;
+
+import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+public class WxOpenComponentAccessToken implements Serializable{
+
+ private String componentAccessToken;
+
+ private int expiresIn = -1;
+
+ public static WxOpenComponentAccessToken fromJson(String json) {
+ return WxOpenGsonBuilder.create().fromJson(json, WxOpenComponentAccessToken.class);
+ }
+
+ public String getComponentAccessToken() {
+ return componentAccessToken;
+ }
+
+ public void setComponentAccessToken(String componentAccessToken) {
+ this.componentAccessToken = componentAccessToken;
+ }
+
+ public int getExpiresIn() {
+ return expiresIn;
+ }
+
+ public void setExpiresIn(int expiresIn) {
+ this.expiresIn = expiresIn;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java
new file mode 100644
index 00000000..687e31ed
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java
@@ -0,0 +1,18 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author 007
+ */
+@Data
+public class WxOpenAuthorizationInfo implements Serializable{
+ private String authorizerAppid;
+ private String authorizerAccessToken;
+ private int expiresIn;
+ private String authorizerRefreshToken;
+ private List funcInfo;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java
new file mode 100644
index 00000000..b9f63a3e
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java
@@ -0,0 +1,22 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author 007
+ */
+@Data
+public class WxOpenAuthorizerInfo implements Serializable{
+ private String nickName;
+ private String headImg;
+ private Integer serviceTypeInfo;
+ private Integer verifyTypeInfo;
+ private String userName;
+ private String principalName;
+ private Map businessInfo;
+ private String alias;
+ private String qrcodeUrl;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
new file mode 100644
index 00000000..afcbd5f1
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
@@ -0,0 +1,100 @@
+package me.chanjar.weixin.open.bean.message;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import lombok.Data;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.util.WxOpenCryptUtil;
+import me.chanjar.weixin.open.util.xml.XStreamTransformer;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+@XStreamAlias("xml")
+@Data
+public class WxOpenXmlMessage implements Serializable{
+ @XStreamAlias("AppId")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String appId;
+
+ @XStreamAlias("CreateTime")
+ private Long createTime;
+
+ @XStreamAlias("InfoType")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String infoType;
+
+ @XStreamAlias("ComponentVerifyTicket")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String componentVerifyTicket;
+
+ @XStreamAlias("AuthorizerAppid")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String authorizerAppid;
+
+ @XStreamAlias("AuthorizationCode")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String authorizationCode;
+
+ @XStreamAlias("AuthorizationCodeExpiredTime")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private Long authorizationCodeExpiredTime;
+
+ @XStreamAlias("PreAuthCode")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String preAuthCode;
+
+ public static WxOpenXmlMessage fromXml(String xml) {
+ //修改微信变态的消息内容格式,方便解析
+ xml = xml.replace("", "");
+ return XStreamTransformer.fromXml(WxOpenXmlMessage.class, xml);
+ }
+
+ public static WxOpenXmlMessage fromXml(InputStream is) {
+ return XStreamTransformer.fromXml(WxOpenXmlMessage.class, is);
+ }
+
+ /**
+ * 从加密字符串转换
+ *
+ * @param encryptedXml 密文
+ * @param wxOpenConfigStorage 配置存储器对象
+ * @param timestamp 时间戳
+ * @param nonce 随机串
+ * @param msgSignature 签名串
+ */
+ public static WxOpenXmlMessage fromEncryptedXml(String encryptedXml,
+ WxOpenConfigStorage wxOpenConfigStorage, String timestamp, String nonce,
+ String msgSignature) {
+ WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage);
+ String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce,
+ encryptedXml);
+ return fromXml(plainText);
+ }
+ public static WxMpXmlMessage fromEncryptedMpXml(String encryptedXml,
+ WxOpenConfigStorage wxOpenConfigStorage, String timestamp, String nonce,
+ String msgSignature) {
+ WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage);
+ String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce,
+ encryptedXml);
+ return WxMpXmlMessage.fromXml(plainText);
+ }
+
+ public static WxOpenXmlMessage fromEncryptedXml(InputStream is,
+ WxOpenConfigStorage wxOpenConfigStorage, String timestamp, String nonce,
+ String msgSignature) {
+ try {
+ return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxOpenConfigStorage,
+ timestamp, nonce, msgSignature);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java
new file mode 100644
index 00000000..040c9f02
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java
@@ -0,0 +1,16 @@
+package me.chanjar.weixin.open.bean.result;
+
+import lombok.Data;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo;
+
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+@Data
+public class WxOpenAuthorizerInfoResult implements Serializable{
+ private WxOpenAuthorizationInfo authorizationInfo;
+ private WxOpenAuthorizerInfo authorizerInfo;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java
new file mode 100644
index 00000000..d7bf7aa1
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java
@@ -0,0 +1,15 @@
+package me.chanjar.weixin.open.bean.result;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+@Data
+public class WxOpenAuthorizerOptionResult implements Serializable{
+ String authorizerAppid;
+ String optionName;
+ String optionValue;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java
new file mode 100644
index 00000000..02dab988
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java
@@ -0,0 +1,14 @@
+package me.chanjar.weixin.open.bean.result;
+
+import lombok.Data;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
+
+import java.io.Serializable;
+
+/**
+ * @author 007
+ */
+@Data
+public class WxOpenQueryAuthResult implements Serializable{
+ private WxOpenAuthorizationInfo authorizationInfo;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java
new file mode 100644
index 00000000..0adcdbb3
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java
@@ -0,0 +1,28 @@
+package me.chanjar.weixin.open.util;
+
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import org.apache.commons.codec.binary.Base64;
+/**
+ * @author 007
+ */
+public class WxOpenCryptUtil extends me.chanjar.weixin.common.util.crypto.WxCryptUtil {
+ /**
+ * 构造函数
+ *
+ * @param wxOpenConfigStorage
+ */
+ public WxOpenCryptUtil(WxOpenConfigStorage wxOpenConfigStorage) {
+ /*
+ * @param token 公众平台上,开发者设置的token
+ * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey
+ * @param appId 公众平台appid
+ */
+ String encodingAesKey = wxOpenConfigStorage.getComponentAesKey();
+ String token = wxOpenConfigStorage.getComponentToken();
+ String appId = wxOpenConfigStorage.getComponentAppId();
+
+ this.token = token;
+ this.appidOrCorpid = appId;
+ this.aesKey = Base64.decodeBase64(encodingAesKey + "=");
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java
new file mode 100644
index 00000000..7aa0c00c
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java
@@ -0,0 +1,45 @@
+package me.chanjar.weixin.open.util.json;
+
+import com.google.gson.*;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 007
+ */
+public class WxOpenAuthorizationInfoGsonAdapter implements JsonDeserializer {
+ @Override
+ public WxOpenAuthorizationInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ WxOpenAuthorizationInfo authorizationInfo = new WxOpenAuthorizationInfo();
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ authorizationInfo.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid"));
+ authorizationInfo.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token"));
+ authorizationInfo.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in"));
+ authorizationInfo.setAuthorizerRefreshToken(GsonHelper.getString(jsonObject, "authorizer_refresh_token"));
+ List funcInfo = new ArrayList<>();
+ JsonArray jsonArray = GsonHelper.getAsJsonArray(jsonObject.get("func_info"));
+ if(jsonArray != null && !jsonArray.isJsonNull()){
+ for(int i = 0; i < jsonArray.size(); i++){
+ jsonObject = jsonArray.get(i).getAsJsonObject();
+ if(jsonObject == null || jsonObject.isJsonNull()){
+ continue;
+ }
+ jsonObject = jsonObject.getAsJsonObject("funcscope_category");
+ if(jsonObject == null || jsonObject.isJsonNull()){
+ continue;
+ }
+ Integer id = GsonHelper.getInteger(jsonObject, "id");
+ if(id == null) {
+ continue;
+ }
+ funcInfo.add(id);
+ }
+ }
+ authorizationInfo.setFuncInfo(funcInfo);
+ return authorizationInfo;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java
new file mode 100644
index 00000000..82a04b9b
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java
@@ -0,0 +1,21 @@
+package me.chanjar.weixin.open.util.json;
+
+import com.google.gson.*;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
+
+import java.lang.reflect.Type;
+
+/**
+ * @author 007
+ */
+public class WxOpenAuthorizerAccessTokenGsonAdapter implements JsonDeserializer {
+ @Override
+ public WxOpenAuthorizerAccessToken deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ WxOpenAuthorizerAccessToken authorizerAccessToken = new WxOpenAuthorizerAccessToken();
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ authorizerAccessToken.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token"));
+ authorizerAccessToken.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in"));
+ return authorizerAccessToken;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java
new file mode 100644
index 00000000..5e1fb6d3
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.open.util.json;
+
+import com.google.gson.*;
+import com.google.gson.reflect.TypeToken;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ * @author 007
+ */
+public class WxOpenAuthorizerInfoGsonAdapter implements JsonDeserializer {
+ @Override
+ public WxOpenAuthorizerInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ WxOpenAuthorizerInfo authorizationInfo = new WxOpenAuthorizerInfo();
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+
+ authorizationInfo.setNickName(GsonHelper.getString(jsonObject, "nick_name"));
+ authorizationInfo.setHeadImg(GsonHelper.getString(jsonObject, "head_img"));
+ authorizationInfo.setUserName(GsonHelper.getString(jsonObject, "user_name"));
+ authorizationInfo.setPrincipalName(GsonHelper.getString(jsonObject, "principal_name"));
+ authorizationInfo.setAlias(GsonHelper.getString(jsonObject, "alias"));
+ authorizationInfo.setQrcodeUrl(GsonHelper.getString(jsonObject, "qrcode_url"));
+ if(jsonObject.has("service_type_info")) {
+ authorizationInfo.setServiceTypeInfo(GsonHelper.getInteger(jsonObject.getAsJsonObject("service_type_info"), "id"));
+ }
+ if(jsonObject.has("verify_type_info")) {
+ authorizationInfo.setVerifyTypeInfo(GsonHelper.getInteger(jsonObject.getAsJsonObject("verify_type_info"), "id"));
+ }
+ Map businessInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("business_info"),
+ new TypeToken