Merge pull request #136 from sutra/wxa

Add WXBizDataCrypt for WeChat Mini Program.
This commit is contained in:
jinyu 2018-05-01 17:33:07 +08:00 committed by GitHub
commit ec04f2302f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 241 additions and 14 deletions

8
.gitignore vendored
View File

@ -12,9 +12,9 @@ hs_err_pid*
*~
# eclipse ignore
*.settings/*
/.project
/.classpath
.settings/
.project
.classpath
/.tomcatplugin
# idea ignore
@ -22,7 +22,7 @@ hs_err_pid*
*.iml
# maven ignore
target/*
target/
# other ignore
*.log

View File

@ -19,6 +19,10 @@ weixin4j
`企业号API封装`
* **weixin4j-wxa[1.8.0]**
`小程序 API 封装`
* **weixin4j-server[1.1.8]**
`netty服务器&消息分发`

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>weixin4j</name>
<url>https://github.com/foxinmy/weixin4j</url>
@ -43,6 +43,7 @@
<module>weixin4j-base</module>
<module>weixin4j-mp</module>
<module>weixin4j-qy</module>
<module>weixin4j-wxa</module>
<module>weixin4j-server</module>
<module>weixin4j-example</module>
<module>weixin4j-serverX</module>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-base</artifactId>
<name>weixin4j-base</name>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<packaging>war</packaging>
<artifactId>weixin4j-example</artifactId>
@ -48,13 +48,13 @@
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-mp</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</dependency>
<!-- 微信企业号 -->
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-qy</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</dependency>
<!-- 微信被动消息(回调模式) -->
<dependency>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-mp</artifactId>
<name>weixin4j-mp</name>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-qy</artifactId>
<name>weixin4j-qy</name>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-server</artifactId>
<version>1.1.9</version>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.7.9</version>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-serverX</artifactId>
<version>0.0.1</version>

49
weixin4j-wxa/pom.xml Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-wxa</artifactId>
<name>weixin4j-wxa</name>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-wxa</url>
<description>微信小程序 API 支持</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.55</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.8</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,60 @@
package com.foxinmy.weixin4j.wxa;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* @since 1.8
*/
final class AESUtils {
private static boolean initialized = false;
private AESUtils() {
}
/**
* AES解密
*
* @param content 密文
* @param keyByte key
* @param ivByte 初始向量
* @return 明文
*/
static byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) {
initialize();
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte)); // 初始化
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static synchronized void initialize() {
if (initialized) {
return;
}
Security.addProvider(new BouncyCastleProvider());
initialized = true;
}
private static AlgorithmParameters generateIV(byte[] iv) throws Exception {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv));
return params;
}
}

View File

@ -0,0 +1,51 @@
package com.foxinmy.weixin4j.wxa;
import java.nio.charset.Charset;
import org.apache.commons.codec.binary.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
* 对微信小程序用户加密数据的解密.
*
* @since 1.8
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject">加密数据解密算法</a>
*/
public class WXBizDataCrypt {
private final String appid;
private final String sessionKey;
public WXBizDataCrypt(String appid, String sessionKey) {
this.appid = appid;
this.sessionKey = sessionKey;
}
/**
* 解密微信小程序用户加密数据.
*
* @param encryptedData 加密的用户数据.
* @param iv 与用户数据一同返回的初始向量.
* @return 解密后的原文.
*/
public JSONObject decryptData(final String encryptedData, final String iv) {
final byte[] aesKey = Base64.decodeBase64(sessionKey);
final byte[] aesCipher = Base64.decodeBase64(encryptedData);
final byte[] aesIV = Base64.decodeBase64(iv);
final byte[] decryptedBytes = AESUtils.decrypt(aesCipher, aesKey, aesIV);
final String decryptedText = new String(decryptedBytes, Charset.forName("UTF-8"));
final JSONObject decrypted = JSON.parseObject(decryptedText);
final String appId = decrypted.getJSONObject("watermark").getString("appid");
if (!appId.equals(this.appid)) {
throw new IllegalArgumentException("Invalid Buffer");
}
return decrypted;
}
}

View File

@ -0,0 +1,7 @@
/**
* <a href="https://developers.weixin.qq.com/miniprogram/dev/api/">WeChat Mini
* Program</a> support library.
*
* @since 1.8
*/
package com.foxinmy.weixin4j.wxa;

View File

@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.wxa;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.alibaba.fastjson.JSONObject;
public class WXBizDataCryptTest {
@Test
public void testDecryptData() {
String appId = "wx4f4bc4dec97d474b";
String sessionKey = "tiihtNczf5v6AKRyjwEUhQ==";
WXBizDataCrypt biz = new WXBizDataCrypt(appId, sessionKey);
String encryptedData
= "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZM"
+ "QmRzooG2xrDcvSnxIMXFufNstNGTyaGS"
+ "9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+"
+ "3hVbJSRgv+4lGOETKUQz6OYStslQ142d"
+ "NCuabNPGBzlooOmB231qMM85d2/fV6Ch"
+ "evvXvQP8Hkue1poOFtnEtpyxVLW1zAo6"
+ "/1Xx1COxFvrc2d7UL/lmHInNlxuacJXw"
+ "u0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn"
+ "/Hz7saL8xz+W//FRAUid1OksQaQx4CMs"
+ "8LOddcQhULW4ucetDf96JcR3g0gfRK4P"
+ "C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB"
+ "6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns"
+ "/8wR2SiRS7MNACwTyrGvt9ts8p12PKFd"
+ "lqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV"
+ "oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG"
+ "20f0a04COwfneQAGGwd5oa+T8yO5hzuy"
+ "Db/XcxxmK01EpqOyuxINew==";
String iv = "r7BXXKkLb8qrSNn05n0qiA==";
JSONObject data = biz.decryptData(encryptedData, iv);
assertEquals("CN", data.getString("country"));
assertEquals("ocMvos6NjeKLIBqg5Mr9QjxrP1FA", data.getString("unionId"));
assertEquals(1, data.getIntValue("gender"));
assertEquals("Guangdong", data.getString("province"));
assertEquals("Guangzhou", data.getString("city"));
assertEquals("http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0", data.getString("avatarUrl"));
assertEquals("oGZUI0egBJY1zhBYw2KhdUfwVJJE", data.getString("openId"));
assertEquals("Band", data.getString("nickName"));
assertEquals("zh_CN", data.getString("language"));
JSONObject watermark = data.getJSONObject("watermark");
assertEquals("wx4f4bc4dec97d474b", watermark.getString("appid"));
assertEquals(1477314187L, watermark.getLongValue("timestamp"));
}
}