| @@ -1,381 +1,387 @@ | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.8.6.BETA</version> | |||
| <packaging>pom</packaging> | |||
| <name>WeiXin Java Tools - Parent</name> | |||
| <description>微信公众号、企业号上级POM</description> | |||
| <url>https://github.com/wechat-group/weixin-java-tools</url> | |||
| <licenses> | |||
| <license> | |||
| <name>The Apache License, Version 2.0</name> | |||
| <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |||
| </license> | |||
| </licenses> | |||
| <developers> | |||
| <developer> | |||
| <name>Daniel Qian</name> | |||
| <email>chanjarster@gmail.com</email> | |||
| <url>https://github.com/chanjarster</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Binary Wang</name> | |||
| <email>binarywang@gmail.com</email> | |||
| <url>https://github.com/binarywang</url> | |||
| </developer> | |||
| <developer> | |||
| <name>gaigeshen</name> | |||
| <email>gaigeshen@qq.com</email> | |||
| <url>https://github.com/gaigeshen</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Liu Mingbo</name> | |||
| <email>liumingbo2008@gmail.com</email> | |||
| <url>https://github.com/FirenzesEagle</url> | |||
| </developer> | |||
| <developer> | |||
| <name>kakotor</name> | |||
| <email>kakotor@gmail.com</email> | |||
| <url>https://github.com/kakotor</url> | |||
| </developer> | |||
| <developer> | |||
| <name>xiong</name> | |||
| <email>zhaoxiong.tan@gmail.com</email> | |||
| <url>https://github.com/ZhaoxiongTan</url> | |||
| </developer> | |||
| <developer> | |||
| <name>LiuJunGuang</name> | |||
| <email>aimilin@yeah.net</email> | |||
| <url>https://github.com/aimilin6688</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Eric.Tsai</name> | |||
| <email>xiaodong.cai.ks@gmail.com</email> | |||
| <url>https://github.com/iwareserictsai</url> | |||
| </developer> | |||
| <developer> | |||
| <name>withinthefog</name> | |||
| <email>withinthefog@gmail.com</email> | |||
| <url>https://github.com/withinthefog</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Keung</name> | |||
| <email>dongfuqiang1988@163.com</email> | |||
| <url>https://github.com/johnnytung</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Jonk</name> | |||
| <email>aimilin@yeah.net</email> | |||
| <url>https://github.com/aimilin6688</url> | |||
| </developer> | |||
| <developer> | |||
| <name>ecoolper</name> | |||
| <email>crskyp@gmail.com</email> | |||
| <url>https://github.com/crskyp</url> | |||
| </developer> | |||
| </developers> | |||
| <scm> | |||
| <connection>scm:git:https://github.com/wechat-group/weixin-java-tools.git</connection> | |||
| <developerConnection>scm:git:git@github.com:wechat-group/weixin-java-tools.git</developerConnection> | |||
| <url>https://github.com/wechat-group/weixin-java-tools</url> | |||
| </scm> | |||
| <modules> | |||
| <module>weixin-java-common</module> | |||
| <module>weixin-java-cp</module> | |||
| <module>weixin-java-mp</module> | |||
| <module>weixin-java-pay</module> | |||
| <module>weixin-java-miniapp</module> | |||
| <!--module>weixin-java-osgi</module--> | |||
| </modules> | |||
| <properties> | |||
| <maven.compiler.source>1.7</maven.compiler.source> | |||
| <maven.compiler.target>1.7</maven.compiler.target> | |||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
| <downloadJavadocs>true</downloadJavadocs> | |||
| <downloadSources>true</downloadSources> | |||
| <httpclient.version>4.5</httpclient.version> | |||
| <jetty.version>9.3.0.RC0</jetty.version> | |||
| </properties> | |||
| <dependencyManagement> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>qrcode-utils</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <!-- 由于jodd-http较新的3.8版本需要jdk8,故而此处采用较低版本 --> | |||
| <dependency> | |||
| <groupId>org.jodd</groupId> | |||
| <artifactId>jodd-http</artifactId> | |||
| <version>3.7.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.squareup.okhttp3</groupId> | |||
| <artifactId>okhttp</artifactId> | |||
| <version>3.7.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpclient</artifactId> | |||
| <version>${httpclient.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpmime</artifactId> | |||
| <version>${httpclient.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-codec</groupId> | |||
| <artifactId>commons-codec</artifactId> | |||
| <version>1.10</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-io</groupId> | |||
| <artifactId>commons-io</artifactId> | |||
| <version>2.5</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| <version>3.5</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.slf4j</groupId> | |||
| <artifactId>slf4j-api</artifactId> | |||
| <version>1.7.24</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.thoughtworks.xstream</groupId> | |||
| <artifactId>xstream</artifactId> | |||
| <version>1.4.9</version> | |||
| </dependency> | |||
| <!-- 由于guava较新的21.0版本需要jdk8,故而此处采用较低版本 --> | |||
| <dependency> | |||
| <groupId>com.google.guava</groupId> | |||
| <artifactId>guava</artifactId> | |||
| <version>20.0</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.code.gson</groupId> | |||
| <artifactId>gson</artifactId> | |||
| <version>2.8.0</version> | |||
| </dependency> | |||
| <!-- 测试所用依赖 --> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <version>2.9.7</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>ch.qos.logback</groupId> | |||
| <artifactId>logback-classic</artifactId> | |||
| <version>1.1.11</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <version>3.0</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <version>6.10</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.mockito</groupId> | |||
| <artifactId>mockito-all</artifactId> | |||
| <version>1.9.5</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>redis.clients</groupId> | |||
| <artifactId>jedis</artifactId> | |||
| <version>2.9.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| <version>1.16.18</version> | |||
| <scope>compile</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| </dependencyManagement> | |||
| <distributionManagement> | |||
| <snapshotRepository> | |||
| <id>ossrh</id> | |||
| <url>https://oss.sonatype.org/content/repositories/snapshots</url> | |||
| </snapshotRepository> | |||
| <repository> | |||
| <id>ossrh</id> | |||
| <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> | |||
| </repository> | |||
| </distributionManagement> | |||
| <profiles> | |||
| <profile> | |||
| <id>doclint-java8-disable</id> | |||
| <activation> | |||
| <jdk>[1.8,)</jdk> | |||
| </activation> | |||
| <properties> | |||
| <javadoc.opts>-Xdoclint:none</javadoc.opts> | |||
| </properties> | |||
| </profile> | |||
| <profile> | |||
| <id>release</id> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-source-plugin</artifactId> | |||
| <version>2.2.1</version> | |||
| <executions> | |||
| <execution> | |||
| <id>attach-sources</id> | |||
| <goals> | |||
| <goal>jar-no-fork</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-javadoc-plugin</artifactId> | |||
| <version>2.9.1</version> | |||
| <executions> | |||
| <execution> | |||
| <id>attach-javadocs</id> | |||
| <goals> | |||
| <goal>jar</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| <configuration> | |||
| <additionalparam>${javadoc.opts}</additionalparam> | |||
| <charset>UTF-8</charset> | |||
| <locale>zh_CN</locale> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-gpg-plugin</artifactId> | |||
| <version>1.6</version> | |||
| <executions> | |||
| <execution> | |||
| <id>sign-artifacts</id> | |||
| <phase>verify</phase> | |||
| <goals> | |||
| <goal>sign</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </profile> | |||
| </profiles> | |||
| <build> | |||
| <pluginManagement> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <version>2.17</version> | |||
| <configuration> | |||
| <skip>true</skip> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </pluginManagement> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.sonatype.plugins</groupId> | |||
| <artifactId>nexus-staging-maven-plugin</artifactId> | |||
| <version>1.6.3</version> | |||
| <extensions>true</extensions> | |||
| <configuration> | |||
| <serverId>ossrh</serverId> | |||
| <nexusUrl>https://oss.sonatype.org/</nexusUrl> | |||
| <autoReleaseAfterClose>true</autoReleaseAfterClose> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-release-plugin</artifactId> | |||
| <version>2.5.1</version> | |||
| <configuration> | |||
| <autoVersionSubmodules>true</autoVersionSubmodules> | |||
| <useReleaseProfile>false</useReleaseProfile> | |||
| <releaseProfiles>release</releaseProfiles> | |||
| <goals>deploy</goals> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-compiler-plugin</artifactId> | |||
| <version>3.6.0</version> | |||
| <configuration> | |||
| <encoding>UTF-8</encoding> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-checkstyle-plugin</artifactId> | |||
| <version>2.17</version> | |||
| <configuration> | |||
| <configLocation>quality-checks/google_checks.xml</configLocation> | |||
| <includeTestSourceDirectory>true</includeTestSourceDirectory> | |||
| <consoleOutput>true</consoleOutput> | |||
| <failsOnError>true</failsOnError> | |||
| </configuration> | |||
| <executions> | |||
| <execution> | |||
| <phase>verify</phase> | |||
| <goals> | |||
| <goal>check</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.8.6.BETA</version> | |||
| <packaging>pom</packaging> | |||
| <name>WeiXin Java Tools - Parent</name> | |||
| <description>微信公众号、企业号上级POM</description> | |||
| <url>https://github.com/wechat-group/weixin-java-tools</url> | |||
| <licenses> | |||
| <license> | |||
| <name>The Apache License, Version 2.0</name> | |||
| <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |||
| </license> | |||
| </licenses> | |||
| <developers> | |||
| <developer> | |||
| <name>Daniel Qian</name> | |||
| <email>chanjarster@gmail.com</email> | |||
| <url>https://github.com/chanjarster</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Binary Wang</name> | |||
| <email>binarywang@gmail.com</email> | |||
| <url>https://github.com/binarywang</url> | |||
| </developer> | |||
| <developer> | |||
| <name>gaigeshen</name> | |||
| <email>gaigeshen@qq.com</email> | |||
| <url>https://github.com/gaigeshen</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Liu Mingbo</name> | |||
| <email>liumingbo2008@gmail.com</email> | |||
| <url>https://github.com/FirenzesEagle</url> | |||
| </developer> | |||
| <developer> | |||
| <name>kakotor</name> | |||
| <email>kakotor@gmail.com</email> | |||
| <url>https://github.com/kakotor</url> | |||
| </developer> | |||
| <developer> | |||
| <name>xiong</name> | |||
| <email>zhaoxiong.tan@gmail.com</email> | |||
| <url>https://github.com/ZhaoxiongTan</url> | |||
| </developer> | |||
| <developer> | |||
| <name>LiuJunGuang</name> | |||
| <email>aimilin@yeah.net</email> | |||
| <url>https://github.com/aimilin6688</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Eric.Tsai</name> | |||
| <email>xiaodong.cai.ks@gmail.com</email> | |||
| <url>https://github.com/iwareserictsai</url> | |||
| </developer> | |||
| <developer> | |||
| <name>withinthefog</name> | |||
| <email>withinthefog@gmail.com</email> | |||
| <url>https://github.com/withinthefog</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Keung</name> | |||
| <email>dongfuqiang1988@163.com</email> | |||
| <url>https://github.com/johnnytung</url> | |||
| </developer> | |||
| <developer> | |||
| <name>Jonk</name> | |||
| <email>aimilin@yeah.net</email> | |||
| <url>https://github.com/aimilin6688</url> | |||
| </developer> | |||
| <developer> | |||
| <name>ecoolper</name> | |||
| <email>crskyp@gmail.com</email> | |||
| <url>https://github.com/crskyp</url> | |||
| </developer> | |||
| <developer> | |||
| <name>007</name> | |||
| <email>007gzs@gmail.com</email> | |||
| <url>https://github.com/007gzs</url> | |||
| </developer> | |||
| </developers> | |||
| <scm> | |||
| <connection>scm:git:https://github.com/wechat-group/weixin-java-tools.git</connection> | |||
| <developerConnection>scm:git:git@github.com:wechat-group/weixin-java-tools.git</developerConnection> | |||
| <url>https://github.com/wechat-group/weixin-java-tools</url> | |||
| </scm> | |||
| <modules> | |||
| <module>weixin-java-common</module> | |||
| <module>weixin-java-cp</module> | |||
| <module>weixin-java-mp</module> | |||
| <module>weixin-java-pay</module> | |||
| <module>weixin-java-miniapp</module> | |||
| <module>weixin-java-open</module> | |||
| <!--module>weixin-java-osgi</module--> | |||
| </modules> | |||
| <properties> | |||
| <maven.compiler.source>1.7</maven.compiler.source> | |||
| <maven.compiler.target>1.7</maven.compiler.target> | |||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
| <downloadJavadocs>true</downloadJavadocs> | |||
| <downloadSources>true</downloadSources> | |||
| <httpclient.version>4.5</httpclient.version> | |||
| <jetty.version>9.3.0.RC0</jetty.version> | |||
| </properties> | |||
| <dependencyManagement> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>qrcode-utils</artifactId> | |||
| <version>1.1</version> | |||
| </dependency> | |||
| <!-- 由于jodd-http较新的3.8版本需要jdk8,故而此处采用较低版本 --> | |||
| <dependency> | |||
| <groupId>org.jodd</groupId> | |||
| <artifactId>jodd-http</artifactId> | |||
| <version>3.7.1</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.squareup.okhttp3</groupId> | |||
| <artifactId>okhttp</artifactId> | |||
| <version>3.7.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpclient</artifactId> | |||
| <version>${httpclient.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.httpcomponents</groupId> | |||
| <artifactId>httpmime</artifactId> | |||
| <version>${httpclient.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-codec</groupId> | |||
| <artifactId>commons-codec</artifactId> | |||
| <version>1.10</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-io</groupId> | |||
| <artifactId>commons-io</artifactId> | |||
| <version>2.5</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| <version>3.5</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.slf4j</groupId> | |||
| <artifactId>slf4j-api</artifactId> | |||
| <version>1.7.24</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.thoughtworks.xstream</groupId> | |||
| <artifactId>xstream</artifactId> | |||
| <version>1.4.9</version> | |||
| </dependency> | |||
| <!-- 由于guava较新的21.0版本需要jdk8,故而此处采用较低版本 --> | |||
| <dependency> | |||
| <groupId>com.google.guava</groupId> | |||
| <artifactId>guava</artifactId> | |||
| <version>20.0</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.code.gson</groupId> | |||
| <artifactId>gson</artifactId> | |||
| <version>2.8.0</version> | |||
| </dependency> | |||
| <!-- 测试所用依赖 --> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <version>2.9.7</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>ch.qos.logback</groupId> | |||
| <artifactId>logback-classic</artifactId> | |||
| <version>1.1.11</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <version>3.0</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <version>6.10</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.mockito</groupId> | |||
| <artifactId>mockito-all</artifactId> | |||
| <version>1.9.5</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>redis.clients</groupId> | |||
| <artifactId>jedis</artifactId> | |||
| <version>2.9.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| <version>1.16.18</version> | |||
| <scope>compile</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| </dependencyManagement> | |||
| <distributionManagement> | |||
| <snapshotRepository> | |||
| <id>ossrh</id> | |||
| <url>https://oss.sonatype.org/content/repositories/snapshots</url> | |||
| </snapshotRepository> | |||
| <repository> | |||
| <id>ossrh</id> | |||
| <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> | |||
| </repository> | |||
| </distributionManagement> | |||
| <profiles> | |||
| <profile> | |||
| <id>doclint-java8-disable</id> | |||
| <activation> | |||
| <jdk>[1.8,)</jdk> | |||
| </activation> | |||
| <properties> | |||
| <javadoc.opts>-Xdoclint:none</javadoc.opts> | |||
| </properties> | |||
| </profile> | |||
| <profile> | |||
| <id>release</id> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-source-plugin</artifactId> | |||
| <version>2.2.1</version> | |||
| <executions> | |||
| <execution> | |||
| <id>attach-sources</id> | |||
| <goals> | |||
| <goal>jar-no-fork</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-javadoc-plugin</artifactId> | |||
| <version>2.9.1</version> | |||
| <executions> | |||
| <execution> | |||
| <id>attach-javadocs</id> | |||
| <goals> | |||
| <goal>jar</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| <configuration> | |||
| <additionalparam>${javadoc.opts}</additionalparam> | |||
| <charset>UTF-8</charset> | |||
| <locale>zh_CN</locale> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-gpg-plugin</artifactId> | |||
| <version>1.6</version> | |||
| <executions> | |||
| <execution> | |||
| <id>sign-artifacts</id> | |||
| <phase>verify</phase> | |||
| <goals> | |||
| <goal>sign</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </profile> | |||
| </profiles> | |||
| <build> | |||
| <pluginManagement> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <version>2.17</version> | |||
| <configuration> | |||
| <skip>true</skip> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </pluginManagement> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.sonatype.plugins</groupId> | |||
| <artifactId>nexus-staging-maven-plugin</artifactId> | |||
| <version>1.6.3</version> | |||
| <extensions>true</extensions> | |||
| <configuration> | |||
| <serverId>ossrh</serverId> | |||
| <nexusUrl>https://oss.sonatype.org/</nexusUrl> | |||
| <autoReleaseAfterClose>true</autoReleaseAfterClose> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-release-plugin</artifactId> | |||
| <version>2.5.1</version> | |||
| <configuration> | |||
| <autoVersionSubmodules>true</autoVersionSubmodules> | |||
| <useReleaseProfile>false</useReleaseProfile> | |||
| <releaseProfiles>release</releaseProfiles> | |||
| <goals>deploy</goals> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-compiler-plugin</artifactId> | |||
| <version>3.6.0</version> | |||
| <configuration> | |||
| <encoding>UTF-8</encoding> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-checkstyle-plugin</artifactId> | |||
| <version>2.17</version> | |||
| <configuration> | |||
| <configLocation>quality-checks/google_checks.xml</configLocation> | |||
| <includeTestSourceDirectory>true</includeTestSourceDirectory> | |||
| <consoleOutput>true</consoleOutput> | |||
| <failsOnError>true</failsOnError> | |||
| </configuration> | |||
| <executions> | |||
| <execution> | |||
| <phase>verify</phase> | |||
| <goals> | |||
| <goal>check</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -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; | |||
| } | |||
| } | |||
| ``` | |||
| @@ -0,0 +1,97 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.8.6.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-open</artifactId> | |||
| <name>WeiXin Java Tools - Open</name> | |||
| <description>微信开放平台Java SDK</description> | |||
| <developers> | |||
| <developer> | |||
| <name>007</name> | |||
| <email>007gzs@gmail.com</email> | |||
| <url>https://github.com/007gzs</url> | |||
| </developer> | |||
| </developers> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.jodd</groupId> | |||
| <artifactId>jodd-http</artifactId> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.squareup.okhttp3</groupId> | |||
| <artifactId>okhttp</artifactId> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>redis.clients</groupId> | |||
| <artifactId>jedis</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>ch.qos.logback</groupId> | |||
| <artifactId>logback-classic</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <configuration> | |||
| <suiteXmlFiles> | |||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
| </suiteXmlFiles> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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); | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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); | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| package me.chanjar.weixin.open.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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; | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenComponentServiceImpl implements WxOpenComponentService { | |||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| private WxOpenService wxOpenService; | |||
| private static final Map<String, WxMpService> 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()); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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<String, Token> authorizerRefreshTokens = new Hashtable<>(); | |||
| private Map<String, Token> authorizerAccessTokens = new Hashtable<>(); | |||
| private Map<String, Token> jsapiTickets = new Hashtable<>(); | |||
| private Map<String, Token> 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<String, Token> 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<String, Token> map, String key){ | |||
| Token token = map.get(key); | |||
| if(token != null){ | |||
| token.expiresTime = 0L; | |||
| } | |||
| } | |||
| private void updateToken(Map<String, Token> 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,164 @@ | |||
| package me.chanjar.weixin.open.api.impl; | |||
| import redis.clients.jedis.Jedis; | |||
| import redis.clients.jedis.JedisPool; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public abstract class WxOpenServiceAbstractImpl<H, P> implements WxOpenService, RequestHttp<H, P> { | |||
| 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, E> T execute(RequestExecutor<T, E> 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenServiceApacheHttpClientImpl extends WxOpenServiceAbstractImpl<CloseableHttpClient, HttpHost> { | |||
| 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); | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| package me.chanjar.weixin.open.api.impl; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenServiceImpl extends WxOpenServiceApacheHttpClientImpl { | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| package me.chanjar.weixin.open.bean; | |||
| import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; | |||
| import java.io.Serializable; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| package me.chanjar.weixin.open.bean; | |||
| import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; | |||
| import java.io.Serializable; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package me.chanjar.weixin.open.bean.auth; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @Data | |||
| public class WxOpenAuthorizationInfo implements Serializable{ | |||
| private String authorizerAppid; | |||
| private String authorizerAccessToken; | |||
| private int expiresIn; | |||
| private String authorizerRefreshToken; | |||
| private List<Integer> funcInfo; | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| package me.chanjar.weixin.open.bean.auth; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| import java.util.Map; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @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<String, Integer> businessInfo; | |||
| private String alias; | |||
| private String qrcodeUrl; | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @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("</PicList><PicList>", ""); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @Data | |||
| public class WxOpenAuthorizerInfoResult implements Serializable{ | |||
| private WxOpenAuthorizationInfo authorizationInfo; | |||
| private WxOpenAuthorizerInfo authorizerInfo; | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package me.chanjar.weixin.open.bean.result; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @Data | |||
| public class WxOpenAuthorizerOptionResult implements Serializable{ | |||
| String authorizerAppid; | |||
| String optionName; | |||
| String optionValue; | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| @Data | |||
| public class WxOpenQueryAuthResult implements Serializable{ | |||
| private WxOpenAuthorizationInfo authorizationInfo; | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| 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 + "="); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenAuthorizationInfoGsonAdapter implements JsonDeserializer<WxOpenAuthorizationInfo> { | |||
| @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<Integer> 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; | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenAuthorizerAccessTokenGsonAdapter implements JsonDeserializer<WxOpenAuthorizerAccessToken> { | |||
| @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; | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenAuthorizerInfoGsonAdapter implements JsonDeserializer<WxOpenAuthorizerInfo> { | |||
| @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<String, Integer> businessInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("business_info"), | |||
| new TypeToken<Map<String, Integer>>() { | |||
| }.getType()); | |||
| authorizationInfo.setBusinessInfo(businessInfo); | |||
| return authorizationInfo; | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| package me.chanjar.weixin.open.util.json; | |||
| import com.google.gson.*; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; | |||
| import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; | |||
| import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenAuthorizerInfoResultGsonAdapter implements JsonDeserializer<WxOpenAuthorizerInfoResult> { | |||
| @Override | |||
| public WxOpenAuthorizerInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||
| WxOpenAuthorizerInfoResult authorizerInfoResult = new WxOpenAuthorizerInfoResult(); | |||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||
| WxOpenAuthorizationInfo authorizationInfo = WxOpenGsonBuilder.INSTANCE.create().fromJson(jsonObject.get("authorization_info"), | |||
| new TypeToken<WxOpenAuthorizationInfo>() { | |||
| }.getType()); | |||
| authorizerInfoResult.setAuthorizationInfo(authorizationInfo); | |||
| WxOpenAuthorizerInfo authorizerInfo = WxOpenGsonBuilder.INSTANCE.create().fromJson(jsonObject.get("authorizer_info"), | |||
| new TypeToken<WxOpenAuthorizerInfo>() { | |||
| }.getType()); | |||
| authorizerInfoResult.setAuthorizerInfo(authorizerInfo); | |||
| return authorizerInfoResult; | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| 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.result.WxOpenAuthorizerOptionResult; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenAuthorizerOptionResultGsonAdapter implements JsonDeserializer<WxOpenAuthorizerOptionResult> { | |||
| @Override | |||
| public WxOpenAuthorizerOptionResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||
| WxOpenAuthorizerOptionResult authorizerOptionResult = new WxOpenAuthorizerOptionResult(); | |||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||
| authorizerOptionResult.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid")); | |||
| authorizerOptionResult.setOptionName(GsonHelper.getString(jsonObject, "option_name")); | |||
| authorizerOptionResult.setOptionValue(GsonHelper.getString(jsonObject, "option_value")); | |||
| return authorizerOptionResult; | |||
| } | |||
| } | |||
| @@ -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.WxOpenComponentAccessToken; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenComponentAccessTokenGsonAdapter implements JsonDeserializer<WxOpenComponentAccessToken> { | |||
| @Override | |||
| public WxOpenComponentAccessToken deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||
| WxOpenComponentAccessToken componentAccessToken = new WxOpenComponentAccessToken(); | |||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||
| componentAccessToken.setComponentAccessToken(GsonHelper.getString(jsonObject, "component_access_token")); | |||
| componentAccessToken.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); | |||
| return componentAccessToken; | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| package me.chanjar.weixin.open.util.json; | |||
| import com.google.gson.Gson; | |||
| import com.google.gson.GsonBuilder; | |||
| 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.auth.WxOpenAuthorizerInfo; | |||
| 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 <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenGsonBuilder { | |||
| public static final GsonBuilder INSTANCE = new GsonBuilder(); | |||
| static { | |||
| INSTANCE.disableHtmlEscaping(); | |||
| INSTANCE.registerTypeAdapter(WxOpenComponentAccessToken.class, new WxOpenComponentAccessTokenGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenAuthorizerAccessToken.class, new WxOpenAuthorizerAccessTokenGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenAuthorizationInfo.class, new WxOpenAuthorizationInfoGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfo.class, new WxOpenAuthorizerInfoGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenQueryAuthResult.class, new WxOpenQueryAuthResultGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfoResult.class, new WxOpenAuthorizerInfoResultGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxOpenAuthorizerOptionResult.class, new WxOpenAuthorizerOptionResultGsonAdapter()); | |||
| } | |||
| public static Gson create() { | |||
| return INSTANCE.create(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package me.chanjar.weixin.open.util.json; | |||
| import com.google.gson.*; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; | |||
| import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class WxOpenQueryAuthResultGsonAdapter implements JsonDeserializer<WxOpenQueryAuthResult> { | |||
| @Override | |||
| public WxOpenQueryAuthResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||
| WxOpenQueryAuthResult queryAuthResult = new WxOpenQueryAuthResult(); | |||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||
| WxOpenAuthorizationInfo authorizationInfo = WxOpenGsonBuilder.INSTANCE.create().fromJson(jsonObject.get("authorization_info"), | |||
| new TypeToken<WxOpenAuthorizationInfo>() { | |||
| }.getType()); | |||
| queryAuthResult.setAuthorizationInfo(authorizationInfo); | |||
| return queryAuthResult; | |||
| } | |||
| } | |||
| @@ -0,0 +1,90 @@ | |||
| package me.chanjar.weixin.open.util.xml; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; | |||
| import java.io.InputStream; | |||
| import java.util.*; | |||
| /** | |||
| * @author <a href="https://github.com/007gzs">007</a> | |||
| */ | |||
| public class XStreamTransformer { | |||
| private static final Map<Class<?>, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); | |||
| static { | |||
| registerClass(WxOpenXmlMessage.class); | |||
| } | |||
| /** | |||
| * xml -> pojo | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T fromXml(Class<T> clazz, String xml) { | |||
| T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); | |||
| return object; | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T fromXml(Class<T> clazz, InputStream is) { | |||
| T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); | |||
| return object; | |||
| } | |||
| /** | |||
| * pojo -> xml | |||
| */ | |||
| public static <T> String toXml(Class<T> clazz, T object) { | |||
| return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
| } | |||
| /** | |||
| * 注册扩展消息的解析器 | |||
| * | |||
| * @param clz 类型 | |||
| * @param xStream xml解析器 | |||
| */ | |||
| private static void register(Class<?> clz, XStream xStream) { | |||
| CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
| } | |||
| /** | |||
| * 会自动注册该类及其子类 | |||
| * | |||
| * @param clz 要注册的类 | |||
| */ | |||
| private static void registerClass(Class<?> clz) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.setClassLoader(Thread.currentThread().getContextClassLoader()); | |||
| xstream.processAnnotations(clz); | |||
| xstream.processAnnotations(getInnerClasses(clz)); | |||
| if (clz.equals(WxOpenXmlMessage.class)) { | |||
| // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId | |||
| xstream.aliasField("MsgID", WxOpenXmlMessage.class, "msgId"); | |||
| } | |||
| register(clz, xstream); | |||
| } | |||
| private static Class<?>[] getInnerClasses(Class<?> clz) { | |||
| Class<?>[] innerClasses = clz.getClasses(); | |||
| if (innerClasses == null) { | |||
| return null; | |||
| } | |||
| List<Class<?>> result = new ArrayList<>(); | |||
| result.addAll(Arrays.asList(innerClasses)); | |||
| for (Class<?> inner : innerClasses) { | |||
| Class<?>[] innerClz = getInnerClasses(inner); | |||
| if (innerClz == null) { | |||
| continue; | |||
| } | |||
| result.addAll(Arrays.asList(innerClz)); | |||
| } | |||
| return result.toArray(new Class<?>[0]); | |||
| } | |||
| } | |||