Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package me.chanjar.weixin.channel.util;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.expectThrows;

import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.crypto.SHA1;
import org.apache.commons.codec.binary.Base64;
import org.testng.annotations.Test;

/**
* {@link WxChCryptUtils} 单元测试
*/
public class WxChCryptUtilsTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 将 Channel 新增单测加入 TestNG suite

weixin-java-channel/pom.xml:131-133 同样固定使用 src/test/resources/testng.xml,而该 suite 的 <classes>weixin-java-channel/src/test/resources/testng.xml:5-9 中没有任何启用的测试类;按 Surefire suite XML 语义(https://maven.apache.org/surefire-archives/surefire-3.5.4/maven-surefire-plugin/examples/testng.html#using-suite-xml-files),启用常规 mvn -pl weixin-java-channel -am test 时既不会执行这个类,也不会执行同 PR 新增的 XmlUtilsTest,导致新增 Channel 单测没有回归保护。请将新增类加入 suite,或移除 suiteXmlFiles 让默认扫描生效。

Useful? React with 👍 / 👎.


private static final String APP_ID = "wx0000000000000002";
private static final String TOKEN = "test_channel_token";
/** 43 位 EncodingAESKey 占位值,非真实密钥 */
private static final String AES_KEY = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY";

private static final Pattern ENCRYPT_PATTERN = Pattern.compile("<Encrypt><!\\[CDATA\\[(.*?)]]></Encrypt>");
private static final Pattern SIGNATURE_PATTERN =
Pattern.compile("<MsgSignature><!\\[CDATA\\[(.*?)]]></MsgSignature>");
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("<TimeStamp>(.*?)</TimeStamp>");
private static final Pattern NONCE_PATTERN = Pattern.compile("<Nonce><!\\[CDATA\\[(.*?)]]></Nonce>");

private WxChCryptUtils cryptUtils() {
WxChannelDefaultConfigImpl config = new WxChannelDefaultConfigImpl();
config.setAppid(APP_ID);
config.setToken(TOKEN);
config.setAesKey(AES_KEY);
return new WxChCryptUtils(config);
}

private String group(Pattern pattern, String text) {
Matcher matcher = pattern.matcher(text);
assertTrue(matcher.find(), "未匹配到期望的节点:" + pattern.pattern());
return matcher.group(1);
}

@Test
public void testEncryptThenDecrypt() {
WxChCryptUtils cryptUtils = cryptUtils();
String plainText = "<xml><ToUserName><![CDATA[" + APP_ID + "]]></ToUserName>"
+ "<MsgType><![CDATA[event]]></MsgType></xml>";

String encryptedXml = cryptUtils.encrypt(plainText);
assertNotNull(encryptedXml);

String encrypt = group(ENCRYPT_PATTERN, encryptedXml);
String signature = group(SIGNATURE_PATTERN, encryptedXml);
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);

assertEquals(signature, SHA1.gen(TOKEN, timestamp, nonce, encrypt));
assertEquals(cryptUtils.decryptXml(signature, timestamp, nonce, encryptedXml), plainText);
}

@Test
public void testDecryptXmlWithWrongSignature() {
WxChCryptUtils cryptUtils = cryptUtils();
String encryptedXml = cryptUtils.encrypt("<xml><MsgType><![CDATA[event]]></MsgType></xml>");
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);

expectThrows(WxRuntimeException.class,
() -> cryptUtils.decryptXml("wrong_signature", timestamp, nonce, encryptedXml));
}

@Test
public void testDecryptWithSessionKey() throws Exception {
byte[] keyBytes = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = "fedcba9876543210".getBytes(StandardCharsets.UTF_8);
String plainText = "{\"openid\":\"o0000000000000000000\"}";

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(ivBytes));
String encryptedData = Base64.encodeBase64String(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));

String decrypted = WxChCryptUtils.decrypt(Base64.encodeBase64String(keyBytes), encryptedData,
Base64.encodeBase64String(ivBytes));
assertEquals(decrypted, plainText);
}

@Test
public void testDecryptWithInvalidSessionKey() {
expectThrows(RuntimeException.class,
() -> WxChCryptUtils.decrypt(Base64.encodeBase64String("short_key".getBytes(StandardCharsets.UTF_8)),
"invalid", Base64.encodeBase64String("fedcba9876543210".getBytes(StandardCharsets.UTF_8))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package me.chanjar.weixin.channel.util;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import me.chanjar.weixin.channel.bean.base.AttrInfo;
import org.testng.annotations.Test;

/**
* {@link XmlUtils} 单元测试
*/
public class XmlUtilsTest {

private static final String XML = "<AttrInfo><attr_key>这是Key</attr_key><attr_value>这是Value</attr_value></AttrInfo>";

@Test
public void testEncode() {
String xml = XmlUtils.encode(new AttrInfo("这是Key", "这是Value"));
assertNotNull(xml);
assertEquals(xml, XML);
}

@Test
public void testEncodeWithObjectMapper() {
String xml = XmlUtils.encode(new XmlMapper(), new AttrInfo("这是Key", "这是Value"));
assertNotNull(xml);
assertEquals(XmlUtils.decode(xml, AttrInfo.class).getKey(), "这是Key");
}

@Test
public void testDecode() {
AttrInfo info = XmlUtils.decode(XML, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "这是Key");
assertEquals(info.getValue(), "这是Value");
}

@Test
public void testDecodeUnknownProperty() {
String xml = "<AttrInfo><attr_key>k</attr_key><unknown>v</unknown></AttrInfo>";
AttrInfo info = XmlUtils.decode(xml, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "k");
assertNull(info.getValue());
}

@Test
public void testDecodeEmptyOrInvalidXml() {
assertNull(XmlUtils.decode((String) null, AttrInfo.class));
assertNull(XmlUtils.decode("", AttrInfo.class));
assertNull(XmlUtils.decode("not a xml", AttrInfo.class));
}

@Test
public void testDecodeWithTypeReference() {
AttrInfo info = XmlUtils.decode(XML, new TypeReference<AttrInfo>() {
});
assertNotNull(info);
assertEquals(info.getValue(), "这是Value");
}

@Test
public void testDecodeInputStream() {
InputStream is = new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8));
AttrInfo info = XmlUtils.decode(is, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "这是Key");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package me.chanjar.weixin.open.bean.message;

import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
import me.chanjar.weixin.open.util.WxOpenCryptUtil;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

/**
* {@link WxOpenXmlMessage} 单元测试
*/
public class WxOpenXmlMessageTest {

private static final String COMPONENT_APP_ID = "wx0000000000000001";
private static final String COMPONENT_TOKEN = "test_component_token";
/** 43 位 EncodingAESKey 占位值,非真实密钥 */
private static final String COMPONENT_AES_KEY = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY";

private static final Pattern ENCRYPT_PATTERN = Pattern.compile("<Encrypt><!\\[CDATA\\[(.*?)]]></Encrypt>");
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("<TimeStamp>(.*?)</TimeStamp>");
private static final Pattern NONCE_PATTERN = Pattern.compile("<Nonce><!\\[CDATA\\[(.*?)]]></Nonce>");

private static final String VERIFY_TICKET_XML = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1413192605</CreateTime>\n"
+ " <InfoType><![CDATA[component_verify_ticket]]></InfoType>\n"
+ " <ComponentVerifyTicket><![CDATA[ticket@@@abcdefg]]></ComponentVerifyTicket>\n"
+ "</xml>";

private WxOpenInMemoryConfigStorage config() {
WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage();
config.setComponentAppId(COMPONENT_APP_ID);
config.setComponentToken(COMPONENT_TOKEN);
config.setComponentAesKey(COMPONENT_AES_KEY);
return config;
}

private String group(Pattern pattern, String text) {
Matcher matcher = pattern.matcher(text);
assertTrue(matcher.find(), "未匹配到期望的节点:" + pattern.pattern());
return matcher.group(1);
}

@Test
public void testFromXmlComponentVerifyTicket() {
WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(VERIFY_TICKET_XML);
assertNotNull(message);
assertEquals(message.getAppId(), COMPONENT_APP_ID);
assertEquals(message.getCreateTime(), Long.valueOf(1413192605L));
assertEquals(message.getInfoType(), "component_verify_ticket");
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
}

@Test
public void testFromXmlAuthorized() {
String xml = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1413192760</CreateTime>\n"
+ " <InfoType><![CDATA[authorized]]></InfoType>\n"
+ " <AuthorizerAppid><![CDATA[wx0000000000000002]]></AuthorizerAppid>\n"
+ " <AuthorizationCode><![CDATA[auth_code_value]]></AuthorizationCode>\n"
+ " <AuthorizationCodeExpiredTime>600</AuthorizationCodeExpiredTime>\n"
+ " <PreAuthCode><![CDATA[pre_auth_code_value]]></PreAuthCode>\n"
+ "</xml>";

WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(xml);
assertNotNull(message);
assertEquals(message.getInfoType(), "authorized");
assertEquals(message.getAuthorizerAppid(), "wx0000000000000002");
assertEquals(message.getAuthorizationCode(), "auth_code_value");
assertEquals(message.getAuthorizationCodeExpiredTime(), Long.valueOf(600L));
assertEquals(message.getPreAuthCode(), "pre_auth_code_value");
}

@Test
public void testFromXmlFastRegisterWeApp() {
String xml = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1535442403</CreateTime>\n"
+ " <InfoType><![CDATA[notify_third_fasteregister]]></InfoType>\n"
+ " <appid>wx0000000000000003</appid>\n"
+ " <status>0</status>\n"
+ " <auth_code>auth_code_value</auth_code>\n"
+ " <msg><![CDATA[OK]]></msg>\n"
+ " <info>\n"
+ " <name><![CDATA[тест]]></name>\n"
+ " </info>\n"
+ "</xml>";

WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(xml);
assertNotNull(message);
assertEquals(message.getSubAppId(), "wx0000000000000003");
assertEquals(message.getRegistAppId(), "wx0000000000000003");
assertEquals(message.getStatus(), 0);
assertEquals(message.getAuthCode(), "auth_code_value");
assertEquals(message.getMsg(), "OK");
assertNotNull(message.getInfo());
assertEquals(message.getInfo().getName(), "тест");
}

@Test
public void testFromXmlInputStream() {
InputStream is = new ByteArrayInputStream(VERIFY_TICKET_XML.getBytes(StandardCharsets.UTF_8));
WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(is);
assertNotNull(message);
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
}

@Test
public void testFromEncryptedXml() {
WxOpenInMemoryConfigStorage config = config();
String encryptedXml = new WxOpenCryptUtil(config).encrypt(VERIFY_TICKET_XML);

String encrypt = group(ENCRYPT_PATTERN, encryptedXml);
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);
String signature = SHA1.gen(COMPONENT_TOKEN, timestamp, nonce, encrypt);

WxOpenXmlMessage message = WxOpenXmlMessage.fromEncryptedXml(encryptedXml, config, timestamp, nonce, signature);
assertNotNull(message);
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
assertEquals(message.getContext(), VERIFY_TICKET_XML);

InputStream is = new ByteArrayInputStream(encryptedXml.getBytes(StandardCharsets.UTF_8));
WxOpenXmlMessage fromStream = WxOpenXmlMessage.fromEncryptedXml(is, config, timestamp, nonce, signature);
assertNotNull(fromStream);
assertEquals(fromStream.getInfoType(), "component_verify_ticket");
}
}
Loading