20 changed files with 774 additions and 600 deletions
			
			
		| @ -0,0 +1,96 @@ | |||
| /* | |||
|  * Copyright (C) 2018 Baidu, Inc. All Rights Reserved. | |||
|  */ | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import org.apache.commons.codec.binary.Base64; | |||
| 
 | |||
| import javax.crypto.Cipher; | |||
| import javax.crypto.spec.IvParameterSpec; | |||
| import javax.crypto.spec.SecretKeySpec; | |||
| import java.nio.charset.Charset; | |||
| import java.util.Arrays; | |||
| 
 | |||
| /** | |||
|  * JAVA AES 消息加解密 | |||
|  */ | |||
| public class AesDecryptUtil { | |||
| 
 | |||
|     private static Charset CHARSET = Charset.forName("utf-8"); | |||
|     private Cipher decCipher; | |||
| 
 | |||
|     /** | |||
|      * 构造函数 | |||
|      * | |||
|      * @param encodingAesKey encodingAESKey | |||
|      * | |||
|      * @throws Exception 异常错误信息 | |||
|      */ | |||
|     public AesDecryptUtil(String encodingAesKey) throws Exception { | |||
|         int encodingAesKeyLength = 43; | |||
|         if (encodingAesKey.length() != encodingAesKeyLength) { | |||
|             throw new Exception("ILLEGAL_AES_KEY_ERROR"); | |||
|         } | |||
|         byte[] aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
| 
 | |||
|         SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); | |||
|         IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); | |||
|         Cipher encCipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
|         encCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); | |||
| 
 | |||
|         decCipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
|         decCipher.init(Cipher.DECRYPT_MODE, keySpec, iv); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 还原4个字节的网络字节序 | |||
|      * | |||
|      * @param orderBytes 字节码 | |||
|      * | |||
|      * @return sourceNumber | |||
|      */ | |||
|     private int recoverNetworkBytesOrder(byte[] orderBytes) { | |||
|         int sourceNumber = 0; | |||
|         int length = 4; | |||
|         int number = 8; | |||
|         for (int i = 0; i < length; i++) { | |||
|             sourceNumber <<= number; | |||
|             sourceNumber |= orderBytes[i] & 0xff; | |||
|         } | |||
|         return sourceNumber; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 对密文进行解密 | |||
|      * | |||
|      * @param text 需要解密的密文 | |||
|      * | |||
|      * @return 解密得到的明文 | |||
|      * | |||
|      * @throws Exception 异常错误信息 | |||
|      */ | |||
|     public String decrypt(String text) | |||
|             throws Exception { | |||
|         byte[] original; | |||
|         try { | |||
| 
 | |||
|             byte[] encrypted = Base64.decodeBase64(text); | |||
|             original = decCipher.doFinal(encrypted); | |||
|         } catch (Exception e) { | |||
|             throw new Exception("DECRYPT_AES_ERROR"); | |||
|         } | |||
|         String xmlContent; | |||
|         try { | |||
|             // 去除补位字符
 | |||
|             byte[] bytes = PKCS7Encoder.decode(original); | |||
|             // 分离16位随机字符串,网络字节序和ClientId
 | |||
|             byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); | |||
|             int xmlLength = recoverNetworkBytesOrder(networkOrder); | |||
|             xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); | |||
|         } catch (Exception e) { | |||
|             throw new Exception("ILLEGAL_BUFFER_ERROR"); | |||
|         } | |||
|         return xmlContent; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,59 @@ | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| @SuppressWarnings("serial") | |||
| public class AesException extends Exception { | |||
| 
 | |||
| 	public final static int OK = 0; | |||
| 	public final static int ValidateSignatureError = -40001; | |||
| 	public final static int ParseXmlError = -40002; | |||
| 	public final static int ComputeSignatureError = -40003; | |||
| 	public final static int IllegalAesKey = -40004; | |||
| 	public final static int ValidateAppidError = -40005; | |||
| 	public final static int EncryptAESError = -40006; | |||
| 	public final static int DecryptAESError = -40007; | |||
| 	public final static int IllegalBuffer = -40008; | |||
| 	//public final static int EncodeBase64Error = -40009;
 | |||
| 	//public final static int DecodeBase64Error = -40010;
 | |||
| 	//public final static int GenReturnXmlError = -40011;
 | |||
| 
 | |||
| 	private int code; | |||
| 
 | |||
| 	private static String getMessage(int code) { | |||
| 		switch (code) { | |||
| 		case ValidateSignatureError: | |||
| 			return "签名验证错误"; | |||
| 		case ParseXmlError: | |||
| 			return "xml解析失败"; | |||
| 		case ComputeSignatureError: | |||
| 			return "sha加密生成签名失败"; | |||
| 		case IllegalAesKey: | |||
| 			return "SymmetricKey非法"; | |||
| 		case ValidateAppidError: | |||
| 			return "appid校验失败"; | |||
| 		case EncryptAESError: | |||
| 			return "aes加密失败"; | |||
| 		case DecryptAESError: | |||
| 			return "aes解密失败"; | |||
| 		case IllegalBuffer: | |||
| 			return "解密后得到的buffer非法"; | |||
| //		case EncodeBase64Error:
 | |||
| //			return "base64加密错误";
 | |||
| //		case DecodeBase64Error:
 | |||
| //			return "base64解密错误";
 | |||
| //		case GenReturnXmlError:
 | |||
| //			return "xml生成失败";
 | |||
| 		default: | |||
| 			return null; // cannot be
 | |||
| 		} | |||
| 	} | |||
| 
 | |||
| 	public int getCode() { | |||
| 		return code; | |||
| 	} | |||
| 
 | |||
| 	AesException(int code) { | |||
| 		super(getMessage(code)); | |||
| 		this.code = code; | |||
| 	} | |||
| 
 | |||
| } | |||
| @ -0,0 +1,26 @@ | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import java.util.ArrayList; | |||
| 
 | |||
| class ByteGroup { | |||
| 	ArrayList<Byte> byteContainer = new ArrayList<Byte>(); | |||
| 
 | |||
| 	public byte[] toBytes() { | |||
| 		byte[] bytes = new byte[byteContainer.size()]; | |||
| 		for (int i = 0; i < byteContainer.size(); i++) { | |||
| 			bytes[i] = byteContainer.get(i); | |||
| 		} | |||
| 		return bytes; | |||
| 	} | |||
| 
 | |||
| 	public ByteGroup addBytes(byte[] bytes) { | |||
| 		for (byte b : bytes) { | |||
| 			byteContainer.add(b); | |||
| 		} | |||
| 		return this; | |||
| 	} | |||
| 
 | |||
| 	public int size() { | |||
| 		return byteContainer.size(); | |||
| 	} | |||
| } | |||
| @ -0,0 +1,67 @@ | |||
| /** | |||
|  * 对公众平台发送给公众账号的消息加解密示例代码. | |||
|  *  | |||
|  * @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
|  */ | |||
| 
 | |||
| // ------------------------------------------------------------------------
 | |||
| 
 | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import java.nio.charset.Charset; | |||
| import java.util.Arrays; | |||
| 
 | |||
| /** | |||
|  * 提供基于PKCS7算法的加解密接口. | |||
|  */ | |||
| class PKCS7Encoder { | |||
| 	static Charset CHARSET = Charset.forName("utf-8"); | |||
| 	static int BLOCK_SIZE = 32; | |||
| 
 | |||
| 	/** | |||
| 	 * 获得对明文进行补位填充的字节. | |||
| 	 *  | |||
| 	 * @param count 需要进行填充补位操作的明文字节个数 | |||
| 	 * @return 补齐用的字节数组 | |||
| 	 */ | |||
| 	static byte[] encode(int count) { | |||
| 		// 计算需要填充的位数
 | |||
| 		int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||
| 		if (amountToPad == 0) { | |||
| 			amountToPad = BLOCK_SIZE; | |||
| 		} | |||
| 		// 获得补位所用的字符
 | |||
| 		char padChr = chr(amountToPad); | |||
| 		String tmp = new String(); | |||
| 		for (int index = 0; index < amountToPad; index++) { | |||
| 			tmp += padChr; | |||
| 		} | |||
| 		return tmp.getBytes(CHARSET); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 删除解密后明文的补位字符 | |||
| 	 *  | |||
| 	 * @param decrypted 解密后的明文 | |||
| 	 * @return 删除补位字符后的明文 | |||
| 	 */ | |||
| 	static byte[] decode(byte[] decrypted) { | |||
| 		int pad = (int) decrypted[decrypted.length - 1]; | |||
| 		if (pad < 1 || pad > 32) { | |||
| 			pad = 0; | |||
| 		} | |||
| 		return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||
| 	 *  | |||
| 	 * @param a 需要转化的数字 | |||
| 	 * @return 转化得到的字符 | |||
| 	 */ | |||
| 	static char chr(int a) { | |||
| 		byte target = (byte) (a & 0xFF); | |||
| 		return (char) target; | |||
| 	} | |||
| 
 | |||
| } | |||
| @ -0,0 +1,63 @@ | |||
| /** | |||
|  * 对公众平台发送给公众账号的消息加解密示例代码. | |||
|  *  | |||
|  * @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
|  */ | |||
| 
 | |||
| // ------------------------------------------------------------------------
 | |||
| 
 | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import org.springframework.stereotype.Component; | |||
| 
 | |||
| import java.security.MessageDigest; | |||
| import java.util.Arrays; | |||
| 
 | |||
| /** | |||
|  * SHA1 class | |||
|  * | |||
|  * 计算公众平台的消息签名接口. | |||
|  */ | |||
| @Component | |||
| public class SHA1 { | |||
| 
 | |||
| 	/** | |||
| 	 * 用SHA1算法生成安全签名 | |||
| 	 * @param token 票据 | |||
| 	 * @param timestamp 时间戳 | |||
| 	 * @param nonce 随机字符串 | |||
| 	 * @param encrypt 密文 | |||
| 	 * @return 安全签名 | |||
| 	 * @throws AesException  | |||
| 	 */ | |||
| 	public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException { | |||
| 		try { | |||
| 			String[] array = new String[]{token, timestamp, nonce, encrypt}; | |||
| 			StringBuffer sb = new StringBuffer(); | |||
| 			// 字符串排序
 | |||
| 			Arrays.sort(array); | |||
| 			for (int i = 0; i < 4; i++) { | |||
| 				sb.append(array[i]); | |||
| 			} | |||
| 			String str = sb.toString(); | |||
| 			// SHA1签名生成
 | |||
| 			MessageDigest md = MessageDigest.getInstance("SHA-1"); | |||
| 			md.update(str.getBytes()); | |||
| 			byte[] digest = md.digest(); | |||
| 
 | |||
| 			StringBuffer hexstr = new StringBuffer(); | |||
| 			String shaHex = ""; | |||
| 			for (int i = 0; i < digest.length; i++) { | |||
| 				shaHex = Integer.toHexString(digest[i] & 0xFF); | |||
| 				if (shaHex.length() < 2) { | |||
| 					hexstr.append(0); | |||
| 				} | |||
| 				hexstr.append(shaHex); | |||
| 			} | |||
| 			return hexstr.toString(); | |||
| 		} catch (Exception e) { | |||
| 			e.printStackTrace(); | |||
| 			throw new AesException(AesException.ComputeSignatureError); | |||
| 		} | |||
| 	} | |||
| } | |||
| @ -0,0 +1,281 @@ | |||
| /** | |||
|  * 对公众平台发送给公众账号的消息加解密示例代码. | |||
|  *  | |||
|  * @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
|  */ | |||
| 
 | |||
| // ------------------------------------------------------------------------
 | |||
| 
 | |||
| /** | |||
|  * 针对org.apache.commons.codec.binary.Base64, | |||
|  * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
|  * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
 | |||
|  */ | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import org.apache.commons.codec.binary.Base64; | |||
| 
 | |||
| import javax.crypto.Cipher; | |||
| import javax.crypto.spec.IvParameterSpec; | |||
| import javax.crypto.spec.SecretKeySpec; | |||
| import java.nio.charset.Charset; | |||
| import java.util.Arrays; | |||
| import java.util.Random; | |||
| 
 | |||
| /** | |||
|  * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). | |||
|  * <ol> | |||
|  * 	<li>第三方回复加密消息给公众平台</li> | |||
|  * 	<li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li> | |||
|  * </ol> | |||
|  * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 | |||
|  * <ol> | |||
|  * 	<li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: | |||
|  *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 | |||
|  * 	<li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> | |||
|  * 	<li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li> | |||
|  * 	<li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li> | |||
|  * </ol> | |||
|  */ | |||
| public class WXBizMsgCrypt { | |||
| 	static Charset CHARSET = Charset.forName("utf-8"); | |||
| 	Base64 base64 = new Base64(); | |||
| 	byte[] aesKey; | |||
| 	String token; | |||
| 	String appId; | |||
| 
 | |||
| 	/** | |||
| 	 * 构造函数 | |||
| 	 * @param token 公众平台上,开发者设置的token | |||
| 	 * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
| 	 * @param appId 公众平台appid | |||
| 	 *  | |||
| 	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
| 	 */ | |||
| 	public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { | |||
| 		if (encodingAesKey.length() != 43) { | |||
| 			throw new AesException(AesException.IllegalAesKey); | |||
| 		} | |||
| 
 | |||
| 		this.token = token; | |||
| 		this.appId = appId; | |||
| 		aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
| 	} | |||
| 
 | |||
| 	// 生成4个字节的网络字节序
 | |||
| 	byte[] getNetworkBytesOrder(int sourceNumber) { | |||
| 		byte[] orderBytes = new byte[4]; | |||
| 		orderBytes[3] = (byte) (sourceNumber & 0xFF); | |||
| 		orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); | |||
| 		orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); | |||
| 		orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); | |||
| 		return orderBytes; | |||
| 	} | |||
| 
 | |||
| 	// 还原4个字节的网络字节序
 | |||
| 	int recoverNetworkBytesOrder(byte[] orderBytes) { | |||
| 		int sourceNumber = 0; | |||
| 		for (int i = 0; i < 4; i++) { | |||
| 			sourceNumber <<= 8; | |||
| 			sourceNumber |= orderBytes[i] & 0xff; | |||
| 		} | |||
| 		return sourceNumber; | |||
| 	} | |||
| 
 | |||
| 	// 随机生成16位字符串
 | |||
| 	String getRandomStr() { | |||
| 		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||
| 		Random random = new Random(); | |||
| 		StringBuffer sb = new StringBuffer(); | |||
| 		for (int i = 0; i < 16; i++) { | |||
| 			int number = random.nextInt(base.length()); | |||
| 			sb.append(base.charAt(number)); | |||
| 		} | |||
| 		return sb.toString(); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 对明文进行加密. | |||
| 	 *  | |||
| 	 * @param text 需要加密的明文 | |||
| 	 * @return 加密后base64编码的字符串 | |||
| 	 * @throws AesException aes加密失败 | |||
| 	 */ | |||
| 	String encrypt(String randomStr, String text) throws AesException { | |||
| 		ByteGroup byteCollector = new ByteGroup(); | |||
| 		byte[] randomStrBytes = randomStr.getBytes(CHARSET); | |||
| 		byte[] textBytes = text.getBytes(CHARSET); | |||
| 		byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); | |||
| 		byte[] appidBytes = appId.getBytes(CHARSET); | |||
| 
 | |||
| 		// randomStr + networkBytesOrder + text + appid
 | |||
| 		byteCollector.addBytes(randomStrBytes); | |||
| 		byteCollector.addBytes(networkBytesOrder); | |||
| 		byteCollector.addBytes(textBytes); | |||
| 		byteCollector.addBytes(appidBytes); | |||
| 
 | |||
| 		// ... + pad: 使用自定义的填充方式对明文进行补位填充
 | |||
| 		byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); | |||
| 		byteCollector.addBytes(padBytes); | |||
| 
 | |||
| 		// 获得最终的字节流, 未加密
 | |||
| 		byte[] unencrypted = byteCollector.toBytes(); | |||
| 
 | |||
| 		try { | |||
| 			// 设置加密模式为AES的CBC模式
 | |||
| 			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
| 			SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); | |||
| 			IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); | |||
| 			cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); | |||
| 
 | |||
| 			// 加密
 | |||
| 			byte[] encrypted = cipher.doFinal(unencrypted); | |||
| 
 | |||
| 			// 使用BASE64对加密后的字符串进行编码
 | |||
| 			String base64Encrypted = base64.encodeToString(encrypted); | |||
| 
 | |||
| 			return base64Encrypted; | |||
| 		} catch (Exception e) { | |||
| 			e.printStackTrace(); | |||
| 			throw new AesException(AesException.EncryptAESError); | |||
| 		} | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 对密文进行解密. | |||
| 	 *  | |||
| 	 * @param text 需要解密的密文 | |||
| 	 * @return 解密得到的明文 | |||
| 	 * @throws AesException aes解密失败 | |||
| 	 */ | |||
| 	String decrypt(String text) throws AesException { | |||
| 		byte[] original; | |||
| 		try { | |||
| 			// 设置解密模式为AES的CBC模式
 | |||
| 			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
| 			SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | |||
| 			IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); | |||
| 			cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||
| 
 | |||
| 			// 使用BASE64对密文进行解码
 | |||
| 			byte[] encrypted = Base64.decodeBase64(text); | |||
| 
 | |||
| 			// 解密
 | |||
| 			original = cipher.doFinal(encrypted); | |||
| 		} catch (Exception e) { | |||
| 			e.printStackTrace(); | |||
| 			throw new AesException(AesException.DecryptAESError); | |||
| 		} | |||
| 
 | |||
| 		String xmlContent, from_appid; | |||
| 		try { | |||
| 			// 去除补位字符
 | |||
| 			byte[] bytes = PKCS7Encoder.decode(original); | |||
| 
 | |||
| 			// 分离16位随机字符串,网络字节序和AppId
 | |||
| 			byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); | |||
| 
 | |||
| 			int xmlLength = recoverNetworkBytesOrder(networkOrder); | |||
| 
 | |||
| 			xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); | |||
| 			from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), | |||
| 					CHARSET); | |||
| 		} catch (Exception e) { | |||
| 			e.printStackTrace(); | |||
| 			throw new AesException(AesException.IllegalBuffer); | |||
| 		} | |||
| 
 | |||
| 		// appid不相同的情况
 | |||
| 		if (!from_appid.equals(appId)) { | |||
| 			throw new AesException(AesException.ValidateAppidError); | |||
| 		} | |||
| 		return xmlContent; | |||
| 
 | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 将公众平台回复用户的消息加密打包. | |||
| 	 * <ol> | |||
| 	 * 	<li>对要发送的消息进行AES-CBC加密</li> | |||
| 	 * 	<li>生成安全签名</li> | |||
| 	 * 	<li>将消息密文和安全签名打包成xml格式</li> | |||
| 	 * </ol> | |||
| 	 *  | |||
| 	 * @param replyMsg 公众平台待回复用户的消息,xml格式的字符串 | |||
| 	 * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp | |||
| 	 * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce | |||
| 	 *  | |||
| 	 * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 | |||
| 	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
| 	 */ | |||
| 	public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException { | |||
| 		// 加密
 | |||
| 		String encrypt = encrypt(getRandomStr(), replyMsg); | |||
| 
 | |||
| 		// 生成安全签名
 | |||
| 		if (timeStamp == "") { | |||
| 			timeStamp = Long.toString(System.currentTimeMillis()); | |||
| 		} | |||
| 
 | |||
| 		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); | |||
| 
 | |||
| 		// System.out.println("发送给平台的签名是: " + signature[1].toString());
 | |||
| 		// 生成发送的xml
 | |||
| 		String result = XMLParse.generate(encrypt, signature, timeStamp, nonce); | |||
| 		return result; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 检验消息的真实性,并且获取解密后的明文. | |||
| 	 * <ol> | |||
| 	 * 	<li>利用收到的密文生成安全签名,进行签名验证</li> | |||
| 	 * 	<li>若验证通过,则提取xml中的加密消息</li> | |||
| 	 * 	<li>对消息进行解密</li> | |||
| 	 * </ol> | |||
| 	 *  | |||
| 	 * @param msgSignature 签名串,对应URL参数的msg_signature | |||
| 	 * @param timeStamp 时间戳,对应URL参数的timestamp | |||
| 	 * @param nonce 随机串,对应URL参数的nonce | |||
| 	 * @param postData 密文,对应POST请求的数据 | |||
| 	 *  | |||
| 	 * @return 解密后的原文 | |||
| 	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
| 	 */ | |||
| 	public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) | |||
| 			throws AesException { | |||
| 		// 提取密文
 | |||
| 		String encrypt = XMLParse.extract(postData); | |||
| 
 | |||
| 		// 验证安全签名
 | |||
| 		String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); | |||
| 		if (!signature.equals(msgSignature)) { | |||
| 			throw new AesException(AesException.ValidateSignatureError); | |||
| 		} | |||
| 		// 解密
 | |||
| 		String result = decrypt(encrypt); | |||
| 		return result; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 验证URL | |||
| 	 * @param msgSignature 签名串,对应URL参数的msg_signature | |||
| 	 * @param timeStamp 时间戳,对应URL参数的timestamp | |||
| 	 * @param nonce 随机串,对应URL参数的nonce | |||
| 	 * @param echoStr 随机串,对应URL参数的echostr | |||
| 	 *  | |||
| 	 * @return 解密之后的echostr | |||
| 	 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
| 	 */ | |||
| 	public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr) | |||
| 			throws AesException { | |||
| 		String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr); | |||
| 
 | |||
| 		if (!signature.equals(msgSignature)) { | |||
| 			throw new AesException(AesException.ValidateSignatureError); | |||
| 		} | |||
| 
 | |||
| 		String result = decrypt(echoStr); | |||
| 		return result; | |||
| 	} | |||
| 
 | |||
| } | |||
| @ -0,0 +1,71 @@ | |||
| /** | |||
|  * 对公众平台发送给公众账号的消息加解密示例代码. | |||
|  *  | |||
|  * @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
|  */ | |||
| 
 | |||
| // ------------------------------------------------------------------------
 | |||
| 
 | |||
| package com.epmet.mpaes; | |||
| 
 | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| import org.w3c.dom.NodeList; | |||
| import org.xml.sax.InputSource; | |||
| 
 | |||
| import javax.xml.parsers.DocumentBuilder; | |||
| import javax.xml.parsers.DocumentBuilderFactory; | |||
| import java.io.StringReader; | |||
| 
 | |||
| /** | |||
|  * XMLParse class | |||
|  * | |||
|  * 提供提取消息格式中的密文及生成回复消息格式的接口. | |||
|  */ | |||
| public class XMLParse { | |||
| 
 | |||
| 	/** | |||
| 	 * 提取出xml数据包中的加密消息 | |||
| 	 * @param xmltext 待提取的xml字符串 | |||
| 	 * @return 提取出的加密消息字符串 | |||
| 	 * @throws AesException  | |||
| 	 */ | |||
| 	public static String extract(String xmltext) throws AesException     { | |||
| //		Object[] result = new Object[3];
 | |||
| 		try { | |||
| 			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||
| 			DocumentBuilder db = dbf.newDocumentBuilder(); | |||
| 			StringReader sr = new StringReader(xmltext); | |||
| 			InputSource is = new InputSource(sr); | |||
| 			Document document = db.parse(is); | |||
| 
 | |||
| 			Element root = document.getDocumentElement(); | |||
| 			NodeList nodelist1 = root.getElementsByTagName("Encrypt"); | |||
| 			NodeList nodelist2 = root.getElementsByTagName("ToUserName"); | |||
| //			result[0] = 0;
 | |||
| 			String result = nodelist1.item(0).getTextContent(); | |||
| //			result[2] = nodelist2.item(0).getTextContent();
 | |||
| 			return result; | |||
| 		} catch (Exception e) { | |||
| 			e.printStackTrace(); | |||
| 			throw new AesException(AesException.ParseXmlError); | |||
| 		} | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 生成xml消息 | |||
| 	 * @param encrypt 加密后的消息密文 | |||
| 	 * @param signature 安全签名 | |||
| 	 * @param timestamp 时间戳 | |||
| 	 * @param nonce 随机字符串 | |||
| 	 * @return 生成的xml字符串 | |||
| 	 */ | |||
| 	public static String generate(String encrypt, String signature, String timestamp, String nonce) { | |||
| 
 | |||
| 		String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | |||
| 				+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
| 				+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>"; | |||
| 		return String.format(format, encrypt, signature, timestamp, nonce); | |||
| 
 | |||
| 	} | |||
| } | |||
| @ -1,30 +0,0 @@ | |||
| package com.epmet.util; | |||
| 
 | |||
| import okhttp3.*; | |||
| import org.springframework.stereotype.Component; | |||
| 
 | |||
| import java.io.IOException; | |||
| 
 | |||
| /** | |||
|  * @Author zxc | |||
|  * @CreateTime 2020/7/7 17:39 | |||
|  */ | |||
| @Component | |||
| public class OkHttpHelper { | |||
| 
 | |||
|     public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); | |||
| 
 | |||
|     OkHttpClient client = new OkHttpClient(); | |||
| 
 | |||
|     public String post(String url, String json) throws IOException { | |||
|         RequestBody body = RequestBody.create(JSON, json); | |||
|         Request request = new Request.Builder() | |||
|                 .url(url) | |||
|                 .post(body) | |||
|                 .build(); | |||
|         try (Response response = client.newCall(request).execute()) { | |||
|             return response.body().string(); | |||
|         } | |||
|     } | |||
| 
 | |||
| } | |||
| @ -1,63 +0,0 @@ | |||
| package com.epmet.util; | |||
| 
 | |||
| import com.epmet.commons.tools.constant.NumConstant; | |||
| 
 | |||
| import java.nio.charset.Charset; | |||
| import java.util.Arrays; | |||
| 
 | |||
| /** | |||
|  * @Author zxc | |||
|  * @CreateTime 2020/7/6 10:45 | |||
|  */ | |||
| public class PKCS7EncoderUtil { | |||
| 
 | |||
|     static Charset CHARSET = Charset.forName("utf-8"); | |||
|     static int BLOCK_SIZE = 32; | |||
| 
 | |||
|     /** | |||
|      * 获得对明文进行补位填充的字节. | |||
|      * | |||
|      * @param count 需要进行填充补位操作的明文字节个数 | |||
|      * @return 补齐用的字节数组 | |||
|      */ | |||
|     static byte[] encode(int count) { | |||
|         // 计算需要填充的位数
 | |||
|         int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||
|         if (amountToPad == NumConstant.ZERO) { | |||
|             amountToPad = BLOCK_SIZE; | |||
|         } | |||
|         // 获得补位所用的字符
 | |||
|         char padChr = chr(amountToPad); | |||
|         String tmp = new String(); | |||
|         for (int index = NumConstant.ZERO; index < amountToPad; index++) { | |||
|             tmp += padChr; | |||
|         } | |||
|         return tmp.getBytes(CHARSET); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 删除解密后明文的补位字符 | |||
|      * | |||
|      * @param decrypted 解密后的明文 | |||
|      * @return 删除补位字符后的明文 | |||
|      */ | |||
|     static byte[] decode(byte[] decrypted) { | |||
|         int pad = (int) decrypted[decrypted.length - 1]; | |||
|         if (pad < NumConstant.ONE || pad > 32) { | |||
|             pad = NumConstant.ZERO; | |||
|         } | |||
|         return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||
|      * | |||
|      * @param a 需要转化的数字 | |||
|      * @return 转化得到的字符 | |||
|      */ | |||
|     static char chr(int a) { | |||
|         byte target = (byte) (a & 0xFF); | |||
|         return (char) target; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -1,246 +0,0 @@ | |||
| package com.epmet.util; | |||
| 
 | |||
| import com.epmet.exception.AesException; | |||
| import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; | |||
| import org.apache.commons.codec.binary.Base64; | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| import org.w3c.dom.NodeList; | |||
| import org.xml.sax.InputSource; | |||
| 
 | |||
| import java.io.StringReader; | |||
| import java.nio.charset.Charset; | |||
| import java.security.MessageDigest; | |||
| import java.util.Arrays; | |||
| 
 | |||
| import javax.crypto.Cipher; | |||
| import javax.crypto.spec.IvParameterSpec; | |||
| import javax.crypto.spec.SecretKeySpec; | |||
| import javax.xml.parsers.DocumentBuilder; | |||
| import javax.xml.parsers.DocumentBuilderFactory; | |||
| 
 | |||
| /** | |||
|  * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). | |||
|  * <ol>  * <li>第三方回复加密消息给公众平台</li>  * <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li> | |||
|  * </ol> | |||
|  * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 | |||
|  * <ol> | |||
|  * <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:  * | |||
|  * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 | |||
|  * <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> | |||
|  * <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li> | |||
|  * <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li> | |||
|  * | |||
|  * </ol> | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * @Author zxc | |||
|  * @CreateTime 2020/7/6 9:51 | |||
|  */ | |||
| public class WXBizMsgCrypt { | |||
|     static Charset CHARSET = Charset.forName("utf-8"); | |||
|     Base64 base64 = new Base64(); | |||
|     byte[] aesKey; | |||
|     String token; | |||
|     String appId; | |||
| 
 | |||
|     /** | |||
|      * 构造函数 | |||
|      * @param token 公众平台上,开发者设置的token | |||
|      * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
|      * @param appId 公众平台appid | |||
|      * | |||
|      * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
|      */ | |||
|     public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { | |||
|         if (encodingAesKey.length() != 43) { | |||
|             throw new AesException(AesException.IllegalAesKey); | |||
|         } | |||
| 
 | |||
|         this.token = token; | |||
|         this.appId = appId; | |||
|         aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
|     } | |||
| 
 | |||
|     // 还原4个字节的网络字节序
 | |||
|     int recoverNetworkBytesOrder(byte[] orderBytes) { | |||
|         int sourceNumber = 0; | |||
|         for (int i = 0; i < 4; i++) { | |||
|             sourceNumber <<= 8; | |||
|             sourceNumber |= orderBytes[i] & 0xff; | |||
|         } | |||
|         return sourceNumber; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 对密文进行解密. | |||
|      * @param text 需要解密的密文 | |||
|      * @return 解密得到的明文 | |||
|      * @throws AesException aes解密失败 | |||
|      */ | |||
|     String decrypt(String text) throws AesException { | |||
|         byte[] original; | |||
|         try { | |||
|             // 设置解密模式为AES的CBC模式
 | |||
|             Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
|             SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | |||
|             IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); | |||
|             cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||
| 
 | |||
|             // 使用BASE64对密文进行解码
 | |||
|             byte[] encrypted = Base64.decodeBase64(text); | |||
| 
 | |||
|             // 解密
 | |||
|             original = cipher.doFinal(encrypted); | |||
|         } catch (Exception e) { | |||
|             e.printStackTrace(); | |||
|             throw new AesException(AesException.DecryptAESError); | |||
|         } | |||
| 
 | |||
|         String xmlContent, from_appid; | |||
|         try { | |||
|             // 去除补位字符
 | |||
|             byte[] bytes = PKCS7Encoder.decode(original); | |||
| 
 | |||
|             // 分离16位随机字符串,网络字节序和AppId
 | |||
|             byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); | |||
| 
 | |||
|             int xmlLength = recoverNetworkBytesOrder(networkOrder); | |||
| 
 | |||
|             xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); | |||
|             from_appid = | |||
|                     new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
|         } catch (Exception e) { | |||
|             e.printStackTrace(); | |||
|             throw new AesException(AesException.IllegalBuffer); | |||
|         } | |||
| 
 | |||
|         // appid不相同的情况
 | |||
|         if (!from_appid.equals(appId)) { | |||
|             throw new AesException(AesException.ValidateSignatureError); | |||
|         } | |||
|         return xmlContent; | |||
| 
 | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  * 检验消息的真实性,并且获取解密后的明文. | |||
|      * <ol> | |||
|      * <li>利用收到的密文生成安全签名,进行签名验证</li> | |||
|      * <li>若验证通过,则提取xml中的加密消息</li> | |||
|      * <li>对消息进行解密</li> | |||
|      * </ol> | |||
|      * | |||
|      * @param msgSignature 签名串,对应URL参数的msg_signature | |||
|      * @param timeStamp 时间戳,对应URL参数的timestamp | |||
|      * @param nonce 随机串,对应URL参数的nonce | |||
|      * @param postData 密文,对应POST请求的数据 | |||
|      * @return 解密后的原文 | |||
|      * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 | |||
|      */ | |||
|     public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) | |||
|             throws AesException { | |||
| 
 | |||
|         // 密钥,公众账号的app secret
 | |||
|         // 提取密文
 | |||
|         Object[] encrypt = extract(postData); | |||
| 
 | |||
|         // 验证安全签名
 | |||
|         String signature = getSHA1(token, timeStamp, nonce, encrypt[1].toString()); | |||
| 
 | |||
|         // 和URL中的签名比较是否相等
 | |||
|         // System.out.println("第三方收到URL中的签名:" + msg_sign);
 | |||
|         // System.out.println("第三方校验签名:" + signature);
 | |||
|         if (!signature.equals(msgSignature)) { | |||
|             throw new AesException(AesException.ValidateSignatureError); | |||
|         } | |||
| 
 | |||
|         // 解密
 | |||
|         String result = decrypt(encrypt[1].toString()); | |||
|         return result; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 提取出xml数据包中的加密消息 | |||
|      * @param xmltext 待提取的xml字符串 | |||
|      * @return 提取出的加密消息字符串 | |||
|      * @throws AesException | |||
|      */ | |||
|     public static Object[] extract(String xmltext) throws AesException { | |||
|         Object[] result = new Object[3]; | |||
|         try { | |||
|             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||
|             dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | |||
|             dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); | |||
|             dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); | |||
|             dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); | |||
|             dbf.setXIncludeAware(false); | |||
|             dbf.setExpandEntityReferences(false); | |||
|             DocumentBuilder db = dbf.newDocumentBuilder(); | |||
|             StringReader sr = new StringReader(xmltext); | |||
|             InputSource is = new InputSource(sr); | |||
|             Document document = db.parse(is); | |||
| 
 | |||
|             Element root = document.getDocumentElement(); | |||
|             NodeList nodelist1 = root.getElementsByTagName("Encrypt"); | |||
|             NodeList nodelist2 = root.getElementsByTagName("ToUserName"); | |||
|             result[0] = 0; | |||
|             result[1] = nodelist1.item(0).getTextContent(); | |||
| 
 | |||
|             //注意这里,获取ticket中的xml里面没有ToUserName这个元素,官网原示例代码在这里会报空
 | |||
|             //空指针,所以需要处理一下
 | |||
|             if (nodelist2 != null) { | |||
|                 if (nodelist2.item(0) != null) { | |||
|                     result[2] = nodelist2.item(0).getTextContent(); | |||
|                 } | |||
|             } | |||
|             return result; | |||
|         } catch (Exception e) { | |||
|             e.printStackTrace(); | |||
|             throw new AesException(AesException.ParseXmlError); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 用SHA1算法生成安全签名 | |||
|      * @param token 票据 | |||
|      * @param timestamp 时间戳 | |||
|      * @param nonce 随机字符串 | |||
|      * @param encrypt 密文 | |||
|      * @return 安全签名 | |||
|      * @throws | |||
|      * AesException | |||
|      */ | |||
|     public static String getSHA1(String token, String timestamp, String nonce, String encrypt) | |||
|             throws AesException { | |||
|         try { | |||
|             String[] array = new String[]{token, timestamp, nonce, encrypt}; | |||
|             StringBuffer sb = new StringBuffer(); | |||
|             // 字符串排序
 | |||
|             Arrays.sort(array); | |||
|             for (int i = 0; i < 4; i++) { | |||
|                 sb.append(array[i]); | |||
|             } | |||
|             String str = sb.toString(); | |||
|             // SHA1签名生成
 | |||
|             MessageDigest md = MessageDigest.getInstance("SHA-1"); | |||
|             md.update(str.getBytes()); | |||
|             byte[] digest = md.digest(); | |||
| 
 | |||
|             StringBuffer hexstr = new StringBuffer(); | |||
|             String shaHex = ""; | |||
|             for (int i = 0; i < digest.length; i++) { | |||
|                 shaHex = Integer.toHexString(digest[i] & 0xFF); | |||
|                 if (shaHex.length() < 2) { | |||
|                     hexstr.append(0); | |||
|                 } | |||
|                 hexstr.append(shaHex); | |||
|             } | |||
|             return hexstr.toString(); | |||
|         } catch (Exception e) { | |||
|             e.printStackTrace(); | |||
|             throw new AesException(AesException.ComputeSignatureError); | |||
|         } | |||
|     } | |||
| } | |||
| @ -1,172 +0,0 @@ | |||
| package com.epmet.util; | |||
| import com.google.common.collect.Lists; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.core.util.QuickWriter; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
| import com.thoughtworks.xstream.io.naming.NoNameCoder; | |||
| import com.thoughtworks.xstream.io.xml.CompactWriter; | |||
| import com.thoughtworks.xstream.io.xml.DomDriver; | |||
| import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; | |||
| import com.thoughtworks.xstream.io.xml.XppDriver; | |||
| 
 | |||
| import java.io.StringWriter; | |||
| import java.io.Writer; | |||
| import java.nio.charset.StandardCharsets; | |||
| import java.util.List; | |||
| 
 | |||
| public class XmlUtil { | |||
| 
 | |||
| 	/** | |||
| 	 * 转换不带CDDATA的XML | |||
| 	 * | |||
| 	 * @return | |||
| 	 * @ | |||
| 	 */ | |||
| 	private static XStream getXStream() { | |||
| 		// 实例化XStream基本对象
 | |||
| 		XStream xstream = new XStream(new DomDriver(StandardCharsets.UTF_8.name(), new NoNameCoder() { | |||
| 			// 不对特殊字符进行转换,避免出现重命名字段时的“双下划线”
 | |||
| 			@Override | |||
| 			public String encodeNode(String name) { | |||
| 				return name; | |||
| 			} | |||
| 		})); | |||
| 		// 忽视XML与JAVABEAN转换时,XML中的字段在JAVABEAN中不存在的部分
 | |||
| 		xstream.ignoreUnknownElements(); | |||
| 		return xstream; | |||
| 	} | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 	/** | |||
| 	 * 转换带CDDATA的XML | |||
| 	 * | |||
| 	 * @return | |||
| 	 * @ | |||
| 	 */ | |||
| 	private static XStream getXStreamWithCData(List<String> ignoreCDATA) { | |||
| 		// 实例化XStream扩展对象
 | |||
| 		XStream xstream = new XStream(new XppDriver() { | |||
| 			// 扩展xstream,使其支持CDATA块
 | |||
| 			@Override | |||
| 			public HierarchicalStreamWriter createWriter(Writer out) { | |||
| 				return new PrettyPrintWriter(out) { | |||
| 					boolean cdata = true; | |||
| 					// 不对特殊字符进行转换,避免出现重命名字段时的“双下划线”
 | |||
| 					@Override | |||
| 					public String encodeNode(String name) { | |||
| 						if(!ignoreCDATA.isEmpty()){ | |||
| 							for(String str:ignoreCDATA){ | |||
| 								if(str.equals(name)){ | |||
| 									cdata=false; | |||
| 									return name; | |||
| 								} | |||
| 							} | |||
| 						} | |||
| 						cdata=true; | |||
| 						return name; | |||
| 					} | |||
| 					// 对xml节点的转换都增加CDATA标记
 | |||
| 					@Override | |||
| 					protected void writeText(QuickWriter writer, String text) { | |||
| 						if (cdata) { | |||
| 							writer.write("<![CDATA["); | |||
| 							writer.write(text); | |||
| 							writer.write("]]>"); | |||
| 						} else { | |||
| 							writer.write(text); | |||
| 						} | |||
| 					} | |||
| 
 | |||
| 
 | |||
| 				}; | |||
| 			} | |||
| 		}); | |||
| 		// 忽视XML与JAVABEAN转换时,XML中的字段在JAVABEAN中不存在的部分
 | |||
| 		xstream.ignoreUnknownElements(); | |||
| 		return xstream; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 以压缩的方式输出XML | |||
| 	 * | |||
| 	 * @param obj | |||
| 	 * @return | |||
| 	 */ | |||
| 	public static String toCompressXml(Object obj) { | |||
| 		XStream xstream = getXStream(); | |||
| 		StringWriter sw = new StringWriter(); | |||
| 		// 识别obj类中的注解
 | |||
| 		xstream.processAnnotations(obj.getClass()); | |||
| 		// 设置JavaBean的类别名
 | |||
| 		xstream.aliasType("xml", obj.getClass()); | |||
| 		xstream.marshal(obj, new CompactWriter(sw)); | |||
| 		return sw.toString(); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 以格式化的方式输出XML | |||
| 	 * | |||
| 	 * @param obj | |||
| 	 * @return | |||
| 	 */ | |||
| 	public static String toXml(Object obj) { | |||
| 		XStream xstream = getXStream(); | |||
| 		// 识别obj类中的注解
 | |||
| 		xstream.processAnnotations(obj.getClass()); | |||
| 		// 设置JavaBean的类别名
 | |||
| 		xstream.aliasType("xml", obj.getClass()); | |||
| 		return xstream.toXML(obj); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 转换成JavaBean | |||
| 	 * | |||
| 	 * @param xmlStr | |||
| 	 * @param cls | |||
| 	 * @return | |||
| 	 */ | |||
| 	@SuppressWarnings("unchecked") | |||
| 	public static <T> T toBean(String xmlStr, Class<T> cls) { | |||
| 		XStream xstream = getXStream(); | |||
| 		// 识别cls类中的注解
 | |||
| 		xstream.processAnnotations(cls); | |||
| 		// 设置JavaBean的类别名
 | |||
| 		xstream.aliasType("xml", cls); | |||
| 		T t = (T) xstream.fromXML(xmlStr); | |||
| 		return t; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 以格式化的方式输出XML | |||
| 	 * | |||
| 	 * @param obj | |||
| 	 * @return | |||
| 	 */ | |||
| 	public static String toXmlWithCData(Object obj,List<String> ignoreCDAA) { | |||
| 		XStream xstream = getXStreamWithCData(ignoreCDAA); | |||
| 		// 识别obj类中的注解
 | |||
| 		xstream.processAnnotations(obj.getClass()); | |||
| 		// 设置JavaBean的类别名
 | |||
| 		xstream.aliasType("xml", obj.getClass()); | |||
| 		return xstream.toXML(obj); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * 转换成JavaBean | |||
| 	 * | |||
| 	 * @param xmlStr | |||
| 	 * @param cls | |||
| 	 * @return | |||
| 	 */ | |||
| 	@SuppressWarnings("unchecked") | |||
| 	public static <T> T toBeanWithCData(String xmlStr, Class<T> cls) { | |||
| 		XStream xstream = getXStreamWithCData(null); | |||
| 		// 识别cls类中的注解
 | |||
| 		xstream.processAnnotations(cls); | |||
| 		// 设置JavaBean的类别名
 | |||
| 		xstream.alias("xml", cls); | |||
| 		T t = (T) xstream.fromXML(xmlStr); | |||
| 		return t; | |||
| 	} | |||
| } | |||
					Loading…
					
					
				
		Reference in new issue