对接第三方平台JAVA接口问题推送和解决

简介:

前言

本节所讲为实际项目中与第三方对接出现的问题最后还是靠老大解决了问题以此作为备忘录,本篇分为三小节,一小节解析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>2752154844@qq.com</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。






本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/7295879.html,如需转载请自行联系原作者

目录
相关文章
|
2天前
|
存储 算法 Java
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
【6月更文挑战第19天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
6 3
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
|
2天前
|
设计模式 Java 容器
在Java中调用一个接口的多个实现类
在Java中调用一个接口的多个实现类
11 4
|
3天前
|
Java 对象存储
java对接阿里云OSS上传
java对接阿里云OSS上传
40 2
|
3天前
|
Java 对象存储
java对接七牛云OSS上传
java对接七牛云OSS上传
11 2
|
4天前
|
Java
Java编程不再难:一文看懂抽象类与接口的区别和联系!
【6月更文挑战第17天】在Java OOP中,抽象类与接口助你构建复杂应用。以图书管理系统为例,抽象类`Book`作为基类提供共享属性和方法,不直接实例化。接口如`HasChapters`和`HasIssues`定义特殊行为。抽象类支持部分实现,单继承,适合共享行为;接口仅含常量和抽象方法,多实现,强调行为规范。通过继承和实现,实现代码复用和系统扩展性。理解两者异同,是提升Java编程能力的关键。
|
4天前
|
Java 开发者 C++
Java面向对象的终极挑战:抽象类与接口的深度解析!
【6月更文挑战第17天】在Java OOP中,抽象类和接口助力代码复用与扩展。抽象类不可实例化,提供通用框架,适合继承;接口包含纯抽象方法,支持多态与松耦合。选择抽象类用于继承已有方法和状态,接口则适用于不相关类共享行为。Java 8后接口能含默认方法,增加设计灵活性。抽象类与接口常结合使用,以实现最佳设计,如`Shape`抽象类实现`Drawable`和`Selectable`接口,展现两者协同优势。理解和熟练运用这对概念是提升代码质量的关键。
|
4天前
|
Java
Java编程界的黑魔法:利用抽象类和接口提升你的代码质量!
【6月更文挑战第17天】在Java中,抽象类和接口是提升代码质量的关键。抽象类通过提供共享接口和部分实现减少冗余,强制子类实现标准,并作为扩展点。接口则定义行为契约,促进多态性、松耦合和易扩展性。两者结合使用,可以在保证灵活性的同时增强代码结构和可维护性,为复杂系统的构建打下坚实基础。
|
4天前
|
Java 测试技术 开发者
超越普通Java开发者:掌握抽象类与接口的高级技巧!
【6月更文挑战第17天】在Java编程中,抽象类和接口是提升代码质量和可维护性的核心要素。通过抽象类实现基类定制,如预设实现并强制子类实现特定方法;接口则提供多继承及默认、静态方法,增强复用和模块化。两者结合使用,抽象类封装状态和行为,接口专注行为契约,利于单元测试中的模拟对象创建。掌握这些技巧,能助你编写出更优雅、高效的代码。
|
4天前
|
存储 Java 开发者
Java编程新视角:抽象类和接口,你不知道的秘密!
【6月更文挑战第17天】在Java中,抽象类与接口是抽象概念的关键工具。抽象类是不可实例化的模板,包含抽象和具体方法,适合有层次结构的继承;接口仅含抽象方法,像契约般规定实现类的行为,适用于无关对象间的统一接口。Java类单继承但可多实现接口,增加设计灵活性。理解并巧妙运用二者,能提升代码质量和可维护性。
|
4天前
|
Java 开发者
别再写错代码了!Java抽象类与接口的正确使用姿势!
【6月更文挑战第17天】在Java中,抽象类与接口助力构建灵活代码结构,提升效率。抽象类用于定义公共行为和属性,适合有层次的对象集合;接口包含抽象方法,实现多态,适合不相关对象集合。通过示例展示了如何创建抽象类和实现接口,强调理解其核心价值和使用场景的重要性,以提升代码质量和设计。正确使用抽象类与接口,让代码从平凡走向专业。