1. 程式人生 > >對接第三方平臺JAVA接口問題推送和解決

對接第三方平臺JAVA接口問題推送和解決

處理 end isp var created esc 再看 名稱 mba

前言

本節所講為實際項目中與第三方對接出現的問題最後還是靠老大解決了問題以此作為備忘錄,本篇分為三小節,一小節解析Java加密接口數據,二小節解析XML文件需註意問題,最後一節則是請求Java Soap協議接口。因為第三方平臺都是采用JAVA語言,所以這種情況應該對大家有所幫助。

DES加密/解密Java接口

關於Java中序列化為XML文件就不搞了,我們首先需要講解的是關於加密問題,由於是第三方是采用的DES加密,所以我們只講解DES,有很多人可能有疑問了,這不過時了麽且不安全,不必糾結,這個也不是你我能決定的問題,不在討論範疇內。剛開始以為只是加密和解密而已,很簡單嘛,況且網上的例子多如牛毛,慢慢發現是我太自信了,過後開始研究Java中加密的實現,網上文檔如此說:Java中默認DES加密方式為ECB(電子密碼本模式),而C#中默認DES加密方式為CBC(加密分組鏈接模式)這二者是最常見的DES加密方式,且加密key都為8位,其他的我們就不看了。而後看過各種Java和C#中DES加密文檔,看到此鏈接文章時心裏開始激動了:http://luanxiyuan.iteye.com/blog/1938348,結果一掃,文中所給的Java加密為CBC模式,而剛好對應C#中默認加密模式,,沒毛病不是我想要的,再看看園中其他文章,恩,也挺好,作者給出了Java中的實現,C#中的實現也給出了,但是下面評論一看,加密或者解密出錯,作者回復:在C#上未實踐。我是沒招了,搞了好久也沒弄出來,最後還是沒弄出來,老大搞定,我就等著吃現成的吧,事後我看了看代碼,然後再次查了查資料發現其中區別,所以在這裏作此備忘錄。對此還特意下了個IDEA,玩玩Java,對比下java中C#中的實現,首先我們來看看Java中實現。原諒我沒接觸過Java,搞了兩個小時才研究明白下載IDEA,破解IDEA,下載JDK,使用IDEA,導入包,調試java,本篇博客名稱可以起名為:從Java到.NET,還是.NET好 ,結果很顯然會噴,因為作為初學者沒深入了解Java,所以還是老老實實起個正經博文名稱。一邊打開IDEA,一邊打開VS,那叫一個卡啊。先看看安裝最新IDEA 2017.2.1版本界面。

技術分享

首先我們來看看在.NET中DES加密中ECB模式的實現。

        public static string Encrypt(string originalString, string key)
        {
            if (String.IsNullOrEmpty(originalString))
            {
                throw new ArgumentNullException
                       ("The string which needs to be encrypted can not be null.
"); } var bytes = Encoding.UTF8.GetBytes(key); DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider() { Mode = CipherMode.ECB, Padding = PaddingMode.None }; MemoryStream memoryStream
= new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(bytes, bytes), CryptoStreamMode.Write); StreamWriter writer = new StreamWriter(cryptoStream); writer.Write(originalString); writer.Flush(); cryptoStream.FlushFinalBlock(); writer.Flush(); return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); }
        public static string Decrypt(string cryptedString, string key)
        {
            if (String.IsNullOrEmpty(cryptedString))
            {
                throw new ArgumentNullException
                   ("The string which needs to be decrypted can not be null.");
            }
            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };
            MemoryStream memoryStream = new MemoryStream
                    (Convert.FromBase64String(cryptedString));
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                cryptoProvider.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
            StreamReader reader = new StreamReader(cryptoStream);
            return reader.ReadToEnd();
        }

調用如下:

var encryptStr = Encrypt("Jeffcky from cnblogs", "88888888");

技術分享

神馬啊,加密的數據長度無效,對數據長度還有要求啊,啥破玩意啊。然後需要刪除填充模式:

            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };

技術分享

好了,沒毛病了,在.NET中DES的ECB模式加密和解密就實現了,到了剛才才發現這麽篇文章:http://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html,文中關鍵點在這裏:ECB模式:電子密本方式,這是JAVA封裝的DES算法的默認模式,就是將數據按照8個字節一段進行DES加密或解密得到一段8個字節的密文或者明文,最後一段不足8個字節,則補足8個字節(註意:這裏就涉及到數據補位了)進行計算,之後按照順序將計算所得的數據連在一起即可,各段數據之間互不影響。然後我們再來看看Java中的實現。

     import javax.crypto.Cipher;
     import javax.crypto.SecretKey;
     import javax.crypto.SecretKeyFactory;
     import javax.crypto.spec.DESKeySpec;
     import java.security.SecureRandom;
     import  org.apache.commons.codec.binary.Base64;

    public class DesAlgorithm {
    private static final String CIPHER_ALGORITHM = "DES/ECB/NoPadding";

    private static SecretKey keyGenerator(String keyStr) throws  Exception {
        DESKeySpec desKey = new DESKeySpec(keyStr.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secureKey = keyFactory.generateSecret(desKey);
        return secureKey;
    }

    private static String paddingChar(String date) {
        if (date.getBytes().length % 8 > 0) {
            for (int i = 0; i < date.getBytes().length % 8; i++) {
                date = " " + date;
            }
        }
        return date;
    }

    public static String encrypt(String data, String key) throws Exception {
        SecretKey desKey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        SecureRandom random = new SecureRandom();
        cipher.init(Cipher.ENCRYPT_MODE, desKey, random);
        byte[] results = cipher.doFinal(paddingChar(data).getBytes());
        return Base64.encodeBase64String(results);
    }

    public static String decrypt(String data, String key) throws Exception {
        SecretKey desKey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return new String(cipher.doFinal(Base64.decodeBase64(data))).trim();
    }
}

調用如下:

public class Main {

    public static void main(String[] args) {
        try {
            String source =
                    "<?xml version=\"1.0\" encoding=\"GBK\"?>\n" +
                            "<REQUEST>\n" +
                            "  <workyear>4</workyear>\n" +
                            "  <sex>0</sex>\n" +
                            "  <name>汪鵬</name>\n" +
                            "  <idtype>1</idtype>\n" +
                            "  <idno>421081199109284899</idno>\n" +
                            "  <brithday>1991-09-28</brithday>\n" +
                            "  <school>北華大學</school>\n" +
                            "  <email>[email protected]</email>\n" +
                            "</REQUEST>";
            String key = "88888888";
            String encryptData = DesAlgorithm.encrypt(source,key);
            System.out.println("加密後: " + encryptData);
            String decryptData = DesAlgorithm.decrypt("hYbvHlD/ZpOBLyjofjVJmE4oAqitG9BAhhhuykmI0sc7C3TCLtuxCmjxp5WB+OXdEuwt1CqQxtLBCxpvsGbQUHw37J2LSABl+Zx4cM6Z8o5X4VdhTibUjryYkVPwYrzHgaiHA4VVDQ7P7RpMTsnFk372ZP1W+fr2UhpHC8hkohyBaOZ1NWOieQQvPvOLErhzcGWcmjUsnjp0vNEfM7y/FRsQhhvTKtRiPWPdRpWZGH+TofsSuhNtmcE61u0tgEhLcOpDvifLS9zGj2F7Jn8nR05Au7/uz5gl8jB6FCHc97YKAPR0jx69egA+MKfv6IYTmpSZSnWJGgFnnP4SpLGnH3+7Mm6uX8ni2sBaM0/9H9YpVgqpXJ2fCw==", key);
            System.out.println("解密後: " + decryptData);
        }
        catch (Exception ex)
        {

        }
    }
}

技術分享

此時我們將加密後的數據利用.NET來進行解密,這裏就有兩個問題需要解決,一個是Java中ECB模式為不填充,第二個上述講到Java中ECB加密時會對數據進行補位,且上述演示例子也是對數據進行了補位且用空字符串,所以在.NET中我們仍然需要加上ECB不填充且數據要補位和Java中一致,所以加密需要高修改為如下:

        public static string Encrypt(string originalString, string key)
        {
            if (String.IsNullOrEmpty(originalString))
            {
                throw new ArgumentNullException
                       ("The string which needs to be encrypted can not be null.");
            }
            byte[] textBytes = Encoding.UTF8.GetBytes(originalString);
            int mod = (8 - (textBytes.Length % 8));
            for (int i = 0; i < mod; i++)
            {
                originalString = " " + originalString;
            }
            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
Padding = PaddingMode.None }; MemoryStream memoryStream
= new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(bytes, bytes), CryptoStreamMode.Write); StreamWriter writer = new StreamWriter(cryptoStream); writer.Write(originalString); writer.Flush(); cryptoStream.FlushFinalBlock(); writer.Flush(); return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); }

因為加密時對數據進行了補位,所以在加密時將補位的空字符串去除,所以上述解密出錯我們只需要利用 Trim() 方法去除空字符串即可,我們看看

        public static string Decrypt(string cryptedString, string key)
        {
            if (String.IsNullOrEmpty(cryptedString))
            {
                throw new ArgumentNullException
                   ("The string which needs to be decrypted can not be null.");
            }

            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };
            MemoryStream memoryStream = new MemoryStream
                    (Convert.FromBase64String(cryptedString));
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                cryptoProvider.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
            StreamReader reader = new StreamReader(cryptoStream);
            return reader.ReadToEnd().Trim();
        }

技術分享

大功告成,Over,就是這麽簡單,解密時通過Tirm()方法去除空字符串即可。

解析XML文件

解析XML文件本沒有任何問題,這是老生常談的問題了,但是還是會遇到沒碰到的問題,作此記錄,請往下看,先聲明一個類:

    public class Blog
    {
        public string Name { get; set; }
    }

序列化和反序列化諾:

        public static string Serializer(Type type, object obj)
        {
            MemoryStream Stream = new MemoryStream();
            XmlSerializer xml = new XmlSerializer(type);
            xml.Serialize(Stream, obj);
            Stream.Position = 0;
            StreamReader sr = new StreamReader(Stream);
            string str = sr.ReadToEnd();

            sr.Dispose();
            Stream.Dispose();

            return str;
        }
        public static object Deserialize(Type type, string xml)
        {
            using (StringReader sr = new StringReader(xml))
            {
                XmlSerializer xmldes = new XmlSerializer(type);
                return xmldes.Deserialize(sr);
            }
        }

技術分享

沒毛病,再看,上述Blog再添加一個屬性並實例化Url:

public string Url { get; set; }
            var blog = new Blog()
            {
                Name = "Jeffcky",
                Url = "https://i.cnblogs.com/EditPosts.aspx?postid=7295879&update=1"
            };

想說明的是Url序列化成了如下情況:

<?xml version="1.0"?>
<Blog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Jeffcky</Name>
  <Url>https://i.cnblogs.com/EditPosts.aspx?postid=7295879&amp;update=1</Url>
</Blog>

&變成了&amp,想必這是XML序列化的規則對有些特殊字符進行了處理。但是在調用Java接口反序列化時對於上述&卻沒進行翻譯,報錯如下,納悶:

The reference to entity "characterEncoding" must end with the ; delimiter 

此時需要將&替換為&amp;同樣對於>或者<亦是如此。

var data = data.Replace("&", "&amp;");

請求Java中SOAP接口

由於未接觸過WebService和Soap,在請求Java上的Soap接口時需要在請求頭中添加 SOAPAction ,當然在C#中的Soap請求也可能要加上如下請求頭。如下:

               using (var httpClient = new HttpClient(handler))
                {
                    var httpContent = new StringContent(“xml”, Encoding.UTF8, "text/xml");
                    httpContent.Headers.Add("SOAPAction", “action”);
                    response = await httpClient.PostAsync("url", httpContent);
                }

總結

對於上述遇到的問題想必有些讀者門已經遇見過了,不喜勿噴。對於解析Java中的加密數據過程可見,何必有語言之爭,多懂一門語言終歸是好的,都是為了更好的發展不是,相煎何太急了,以上所有以此作為備忘錄,現在想來對接真不是想象的那麽簡單啊,程序員都認為自己寫的代碼沒有任何問題,有時候還是好生交流才是上策啊,首先懷疑是不是自身這邊是不是出了問題再言其他,而非一棒子直接打死是對方的問題。see u。

對接第三方平臺JAVA接口問題推送和解決