記錄操作系統在字符編碼(String.getBytes())的一個詭異經歷

問題起源

自自動化測試用例接入雲測平臺後,出現一兩條用例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>

裏面主要有兩個字段加密了,分別爲originalDatasignedData,而這兩個字段均爲new String(Base64.encodeBase64(bytes), 'utf-8')加密的,那我就先用工具對originalDatabase64解密一下,出現爲:

<?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對於字符處理更高級?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章