记java之String.getBytes()遇到的字符编码的坑
问题起源自自动化测试用例接入云测平台后,出现一两条用例100%失败,出现以下现象(同一套测试代码):1.本机eclipse、IntelliJ IDEA (windows 7)都可以跑通2.执行自动化的虚拟机eclipse(windows)跑不通3.执行自动化的虚拟机IntelliJ IDEA(windows server 2008 R2)可以跑通4.通过j...
问题起源
自自动化测试用例接入云测平台后,出现一两条用例100%失败,出现以下现象(同一套测试代码):
1.本机eclipse、IntelliJ IDEA (windows 7)都可以跑通
2.执行自动化的虚拟机eclipse(windows)跑不通
3.执行自动化的虚拟机IntelliJ IDEA(windows server 2008 R2)可以跑通
4.通过jenkins集成跑不通
问题重现
- 使用自动化的虚拟机eclipse发起1笔交易
- 观察被测系统服务器的日志(公司通过splunk可以快捷查询)
2018-09-04 09:55:43,557 INFO [tomcat-threads--33] (com.xx.xx.xx.api.impl.xx.pay(xx.java:110))- pki unseal cost120
2018-09-04 09:55:43,557 INFO [tomcat-threads--33] (com.xx.xx.xx.api.impl.xx.pay(xx.java:116))- requestXml is <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pay2BankOrder>
<orderId>TA20180904095502884</orderId>
<bankName>����</bankName>
<branchName>�Ƕ�֧��</branchName>
<creditName>�����</creditName>
<mobile>13400053487</mobile>
<bankAcctId>6222090253475890</bankAcctId>
<amount>2000</amount>
<province>����</province>
<city>�Ͼ�</city>
<remark>5�¹���</remark>
<feeAction>1</feeAction>
</pay2BankOrder>
问题分析
由此可以断定为字符编码
问题,于是回到自动化测试脚本,发现脚本中有多处出现字符编码,以下为测试代码片段:
private static String genPKIMsg(Map<String, String> data) {
String currentTime = DateUtil.getCurrentDate(DateUtil.YYYYMMDDHHMMSS);
String orderId = "TA" + currentTime + String.valueOf(Math.abs(new Random().nextInt()) % 1000);
data.put("orderId", orderId);
Pay2bankOrder pay2bankOrder = new Pay2bankOrder();
CommonUtil.copyProperties(pay2bankOrder, data);
String reqOriginalXml = XmlUtil.convertToXml(pay2bankOrder, "UTF-8"); // 第1次编码
Reporter.log("请求明文报文: " + reqOriginalXml);
// 加签、加密
Mpf mpf = new Mpf();
mpf.setFeatureCode(CommonUtil.getDataDrivenValue(data, "featureCode"));
mpf.setMemberCode(CommonUtil.getDataDrivenValue(data, "memberCode"));
SealedData sealedData = null;
try {
ICryptoService service = CryptoServiceFactory.createCryptoService();
sealedData = service.seal(mpf, reqOriginalXml.getBytes()); // 第2次编码
} catch (Exception e) {
Reporter.FALSE(e.getMessage());
}
Pay2bankRequest request = genRequest(data.get("memberCode"));
byte[] nullByte = {};
byte[] byteOri = sealedData.getOriginalData() == null ? nullByte : sealedData.getOriginalData();
byte[] byteEnc = sealedData.getEncryptedData() == null ? nullByte : sealedData.getEncryptedData();
byte[] byteEnv = sealedData.getDigitalEnvelope() == null ? nullByte : sealedData.getDigitalEnvelope();
byte[] byteSig = sealedData.getSignedData() == null ? nullByte : sealedData.getSignedData();
request.getRequestBody().getSealDataType().setOriginalData(PKIUtil.byte2UTF8StringWithBase64(byteOri)); // 第3次编码
request.getRequestBody().getSealDataType().setSignedData(PKIUtil.byte2UTF8StringWithBase64(byteSig));
request.getRequestBody().getSealDataType().setEncryptedData(PKIUtil.byte2UTF8StringWithBase64(byteEnc));
request.getRequestBody().getSealDataType().setDigitalEnvelope(PKIUtil.byte2UTF8StringWithBase64(byteEnv));
String requestXml = XmlUtil.convertToXml(request, "UTF-8"); // 第4次编码
Reporter.log("请求加密报文: " + requestXml);
return requestXml;
}
我是从下往上分析,既然服务器接收的是乱码的请求,那我就先把加密报文拿出来分析一下,以下为加密报文:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pay2BankRequest>
<pay2bankHead>
<version>1.0</version>
<memberCode>10013538466</memberCode>
</pay2bankHead>
<requestBody>
<sealDataType>
<originalData>PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pgo8cGF5MkJhbmtPcmRlcj4KICAgIDxvcmRlcklkPlRBMjAxODA5MDQwOTE0MjQzNzI8L29yZGVySWQ+CiAgICA8YmFua05hbWU+1dDQ0DwvYmFua05hbWU+CiAgICA8YnJhbmNoTmFtZT6zx7ar1qfQ0DwvYnJhbmNoTmFtZT4KICAgIDxjcmVkaXROYW1lPrqryum+6jwvY3JlZGl0TmFtZT4KICAgIDxtb2JpbGU+MTM0MDAwNTM0ODc8L21vYmlsZT4KICAgIDxiYW5rQWNjdElkPjYyMjIwOTAyNTM0NzU4OTA8L2JhbmtBY2N0SWQ+CiAgICA8YW1vdW50PjIwMDA8L2Ftb3VudD4KICAgIDxwcm92aW5jZT69rcvVPC9wcm92aW5jZT4KICAgIDxjaXR5PsTPvqk8L2NpdHk+CiAgICA8cmVtYXJrPjXUwrmk18o8L3JlbWFyaz4KICAgIDxmZWVBY3Rpb24+MTwvZmVlQWN0aW9uPgo8L3BheTJCYW5rT3JkZXI+Cg==</originalData>
<signedData>KE+NCx1uXlvgKE5hpXd50A==</signedData>
<encryptedData></encryptedData>
<digitalEnvelope></digitalEnvelope>
</sealDataType>
</requestBody>
</pay2BankRequest>
里面主要有两个字段加密了,分别为originalData
和signedData
,而这两个字段均为new String(Base64.encodeBase64(bytes), 'utf-8')
加密的,那我就先用工具对originalData
base64解密一下,出现为:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pay2BankOrder>
<orderId>TA20180904091424372</orderId>
<bankName>ÕÐÐÐ</bankName>
<branchName>³Ç¶«Ö§ÐÐ</branchName>
<creditName>º«Êé¾ê</creditName>
<mobile>13400053487</mobile>
<bankAcctId>6222090253475890</bankAcctId>
<amount>2000</amount>
<province>½ËÕ</province>
<city>ÄϾ©</city>
<remark>5Ô¹¤×Ê</remark>
<feeAction>1</feeAction>
</pay2BankOrder>
乱码了,而byteOri
的数据从sealedData = service.seal(mpf, reqOriginalXml.getBytes());
来的,于是我就去研究了下getBytes()方法,以下为官方javadoc
/**
* Encodes this {@code String} into a sequence of bytes using the
* platform's default charset, storing the result into a new byte array.
*
* <p> The behavior of this method when this string cannot be encoded in
* the default charset is unspecified. The {@link
* java.nio.charset.CharsetEncoder} class should be used when more control
* over the encoding process is required.
*
* @return The resultant byte array
*
* @since JDK1.1
*/
public byte[] getBytes() {
return StringCoding.encode(value, offset, count);
}
大致意思是String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组,也就是说在不同的操作系统返回的不一样,参考链接:https://www.cnblogs.com/jiayouxiage/p/6120604.html
问题修正
sealedData = service.seal(mpf, reqOriginalXml.getBytes());
改为
sealedData = service.seal(mpf, reqOriginalXml.getBytes("UTF-8"));
原因:既然后面使用new String(Base64.encodeBase64(bytes), 'utf-8')
utf-8还原数据,那前面也要使用utf-8
处理数据,因为受操作系统影响,那我们就强制加上utf-8
,加上重新跑一下,用例正常
遗留一个疑问:
如果说操作系统影响了编码,我本地的eclipse能跑通(windows7),那为何虚拟机的idea也能跑通,难道idea对于字符处理更高级?
更多推荐
所有评论(0)