python 调用java编写的webservice接口 总结

场景:

  1. 调用其它公司的接口, 给出的文档如下:

所有接口均使用webservice方式提供,遵守soap协议,接口输入输出通过xml格式的string进行调用。通过post方式提交XML报文数据到接口,我司系统接收并返回XML报文数据,完成报文数据交换;我司是通过Axis2的形式实现的webservice服务.

java编写的代码

测试方法

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.CountDownLatch;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.ndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;

import com.ab.mediation.util.DesUtil;
import com.tianbo.util.Encryption;
  
public class Test{  
 
  public static void main(String[] args){   
	    try {   
			// 初始化读数据流对象reader
		  File f = new File(mInFile);
			InputStreamReader reader = new InputStreamReader(new FileInputStream(
					mInFile),  "GBK");
			// 根据f文件长度初始化字符串数据c[]
			char c[] = new char[(int) (f.length())];
			// 取到字符串长度,并将文件f内容写入数组c
			int length = reader.read(c);
			String tDoc = "";// 请求报文
			// 逐字节将字符串数组c[],赋给变量tDoc
			for (int i = 0; i < length; i++) {
				tDoc = tDoc + c[i];
			}
	 

		    System.out.println(tDoc);
		    tDoc = DesUtil.encrypt(tDoc,"defaultmaybe20bytes","GBK"); # 此处敏感信息脱敏
		    System.out.println(tDoc);

		    String url = "http://192.168.100.100:7001/services/openService?wsdl";  # 此处敏感信息脱敏
	  
	      // 使用RPC方式调用WebService   
	      RPCServiceClient serviceClient = new RPCServiceClient();   
	      // 指定调用WebService的URL   
	      EndpointReference targetEPR = new EndpointReference(url);   
	      Options options = serviceClient.getOptions();   
	      //确定目标服务地址   
	      options.setTo(targetEPR);   
	      //确定调用方法   
	      options.setAction("method");   # 此处敏感信息脱敏
	  
	      /**  
	       * 指定要调用的getPrice方法及WSDL文件的命名空间  
	       * 如果 webservice 服务端由axis2编写  
	       * 命名空间 不一致导致的问题  
	       * org.apache.axis2.AxisFault: java.lang.RuntimeException: Unexpected subelement arg0  
	       */  
	      QName qname = new QName("http://service.testcom", "method");   # 此处敏感信息脱敏
	      // 指定getPrice方法的参数值   
	      Object[] parameters = new Object[] { tDoc };   
	         
	      // 指定getPrice方法返回值的数据类型的Class对象   
	      Class[] returnTypes = new Class[] { double.class };   
	  
	      // 调用方法一 传递参数,调用服务,获取服务返回结果集   
	      OMElement element = serviceClient.invokeBlocking(qname, parameters);   
	      //值得注意的是,返回结果就是一段由OMElement对象封装的xml字符串。   
	      //我们可以对之灵活应用,下面我取第一个元素值,并打印之。因为调用的方法返回一个结果   
	      String result = element.getFirstElement().getText();   
	      System.out.println(DesUtil.decrypt(result,"defaultmaybe20bytes","GBK"));   # 此处敏感信息脱敏
	  
	      // 调用方法二 getPrice方法并输出该方法的返回值   
	      Object[] response = serviceClient.invokeBlocking(qname, parameters, returnTypes);   
	      // String r = (String) response[0];   
	      Double r = (Double) response[0];   
	      System.out.println(r);   
	  
	    } catch (AxisFault e) {   
	      e.printStackTrace();   
	    }    
  }   
}  

第38行的DesUtil模块

代码如下:

package com.ab.mediation.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
 * 对外接口数据加密/解密类 
 * @author 
 *
 */
public class DesUtil {

	private final static String DES = "DES";

	public static void main(String[] args) throws Exception {
		String tDoc = "";// 请求报文
		String encoding = "GBK";
		// 将函数参数赋给本地参数
		String path = "D:\\11140399001500000835.xml";
		// String path = "F:\\testxml\\requestAppPolInp881.xml";
		String path1 = path;
		// 初始化文件对象f
		File f = new File(path1);
		// 初始化读数据流对象reader
		InputStreamReader reader = new InputStreamReader(new FileInputStream(
				path1), encoding);
		// 根据f文件长度初始化字符串数据c[]
		char c[] = new char[(int) (f.length())];
		// 取到字符串长度,并将文件f内容写入数组c
		int length = reader.read(c);
		// 逐字节将字符串数组c[],赋给变量tDoc
		for (int i = 0; i < length; i++) {
			tDoc = tDoc + c[i];
		}
		// System.out.println(tDoc);

		String key = "12dc293d43db3b237849";
		System.out.println(encrypt(tDoc, key));
		System.out.println(decrypt(encrypt(tDoc, key), key));

	}

	/**
	 * Description 根据键值进行加密
	 * 
	 * @param data
	 * @param key
	 *            加密键byte数组
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(String data, String key) throws Exception {
		byte[] bt = encrypt(data.getBytes(), key.getBytes());
		String strs = new BASE64Encoder().encode(bt);
		return strs;
	}
	
	/**
	 * 指定字符编码方式并加密
	 * @param data
	 * @param key
	 * @param encoding
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(String data, String key, String encoding) throws Exception {
		byte[] bt = encrypt(data.getBytes(encoding), key.getBytes());
		String strs = new BASE64Encoder().encode(bt);
		return strs;
	}

	/**
	 * Description 根据键值进行解密
	 * 
	 * @param data
	 * @param key
	 *            加密键byte数组
	 * @return
	 * @throws IOException
	 * @throws Exception
	 */
	public static String decrypt(String data, String key) throws IOException,
			Exception {
		if (data == null)
			return null;
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] buf = decoder.decodeBuffer(data);
		byte[] bt = decrypt(buf, key.getBytes());
		return new String(bt);
	}
	
	/**
	 * 根据键值解密并返回指定编码方式字符串
	 * @param data
	 * @param key
	 * @param encoding
	 * @return
	 * @throws IOException
	 * @throws Exception
	 */
	public static String decrypt(String data, String key, String encoding) throws IOException,
		Exception {
		if (data == null)
			return null;
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] buf = decoder.decodeBuffer(data);
		byte[] bt = decrypt(buf, key.getBytes());
		return new String(bt, encoding);
	}

	/**
	 * Description 根据键值进行加密
	 * 
	 * @param data
	 * @param key
	 *            加密键byte数组
	 * @return
	 * @throws Exception
	 */
	private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
		// 生成一个可信任的随机数源
		SecureRandom sr = new SecureRandom();

		// 从原始密钥数据创建DESKeySpec对象
		DESKeySpec dks = new DESKeySpec(key);

		// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
		SecretKey securekey = keyFactory.generateSecret(dks);

		// Cipher对象实际完成加密操作
		Cipher cipher = Cipher.getInstance(DES);

		// 用密钥初始化Cipher对象
		cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);

		return cipher.doFinal(data);
	}

	/**
	 * Description 根据键值进行解密
	 * 
	 * @param data
	 * @param key
	 *            加密键byte数组
	 * @return
	 * @throws Exception
	 */
	private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
		// 生成一个可信任的随机数源
		SecureRandom sr = new SecureRandom();

		// 从原始密钥数据创建DESKeySpec对象
		DESKeySpec dks = new DESKeySpec(key);

		// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
		SecretKey securekey = keyFactory.generateSecret(dks);

		// Cipher对象实际完成解密操作
		Cipher cipher = Cipher.getInstance(DES);

		// 用密钥初始化Cipher对象
		cipher.init(Cipher.DECRYPT_MODE, securekey, sr);

		return cipher.doFinal(data);
	}
}

python 调用接口

1. xml 文档样例

<?xml version="1.0" encoding="GBK"?>
<node>
	{a}
</node>

2. 调用样例

import arrow
from pyDes import des, PAD_PKCS5, ECB
import base64
import suds
from suds.client import Client
conf = {
    'DEV': 'http://192.168.100.100:7001/services/openService?wsdl',
    'DAT': 'http://192.168.100.100:7001/services/openService?wsdl',
    'UAT': 'http://192.168.100.100:7001/services/openService?wsdl',
    'VIR': 'http://192.168.100.100:7001/services/openService?wsdl',
    'PRO': 'http://192.168.100.100:7001/services/openService?wsdl',
}

class apicall():
    """
    parmarters
    -------------
    env: string 必填 default DAT
        数据环境

    key: string 非必填 default 已写入
        保险公司给出的KEY
    """
    def __init__(self, *args, **kwargs):
        self.env = kwargs.get('env', 'DAT')
        self.url = conf.get(self.env)
        self.xml_file = 'd://test.xml'
        # 这是给出的KEY, 可能是多于8位的
        self.key = kwargs.get('key', 'defaultmaybe20bytes')  # 此处脱敏
        # 这是8字节的KEY
        self.key = self.key[:8]  # 不用怀疑, 真的是切片前8位.
        self.des_obj = des(self.key, ECB, self.key, padmode=PAD_PKCS5)  # 初始化一个des对象,参数是秘钥,加密方式,偏移, 填充方式
        self.client = Client(self.url)

    def encrypt_des(self, text, encoding="GBK"):
        """
        加密的方法
        parmarters
        ------
        text: string
            需要加密的明文
        """
        text = text.encode(encoding)  # 对获取到的明文进行GBK编码
        return base64.b64encode(self.des_obj.encrypt(text))

    def decrypt_des(self, CipherText, encoding="GBK"):
        """
        解密的方法
        parmarters
        ------
        CipherText: string
            需要解密的密文
        """
        return self.des_obj.decrypt(base64.b64decode(CipherText)).decode(encoding)
    
    @abstractmethod
    def post_data(self, *args, **kwargs):
        """
        续期查询接口 依据文档的xml格式
        parmarters
        -------------
        """
        xml_str = ''
        with open(self.xml_file, 'r') as f:
            for l in f.readlines():
                xml_str += l.strip()
        data = xml_str.format(a=a) # 此处脱敏, 主要是在xml的参数位置使用了python的占位符方法(类似这样的 {a})
        print('请求的明文是: \n', data)
        CipherText = self.encrypt_des(data) # 对明文进行加密
        # print(CipherText)
        try:
            r = self.client.service.method(str(CipherText, encoding='utf-8')) # 依据文档: 要求是字符串形式请求
        except Exception as e:
            print('报错:\n', e)
        else:
            return self.decrypt_des(r)  # 对返回数据进行解密

总结:

  1. 对方公司给出的KEY可能是任意位数的, 但java的DES加密方法可以使用这个KEY进行数据加解密, 但python的pyDes文档要求KEY是8位的,怎么办?
    答: 通过查看java的DES文档, 发现也是截取了前8位, 因此在python中key = key[:8]就可以解决问题.

  2. 我也是第一次调用webservice这类接口, 一脸懵逼, 试着去请求一些公用的webservice接口, 比如天气预报类的, 手机号码归属地类的,先确认自己的调用方法是没问题的.再下一步.

  3. 对方文档有N多个接口的对接方法, 但其实只有几个是有权限的, 结果需求不同步, 在没有权限的接口上联调了好久, 最后才发现没有权限请求,哭死了. 还好, 总算对接OK了.

  4. self.client.service.method中的method怎么来的?
    print(self.client)一下, 就可以看到, Methods 中有各种方法
    我的返回的是这样的

Suds ( https://github.com/cackharot/suds-py3 )  version: 1.4.1.0 IN  build: 20200421

Service ( openServiceForLife ) tns="http://service. test.com"  # 此处脱敏
   Prefixes (1)
      ns0 ="http://service. test.com"  # 此处脱敏
   Ports (2):
      (openServiceHttpSoap11Endpoint)
         Methods (1):
            method(xs:string args0, )  # 此处脱敏
         Types (1):
            Exception
      (openServiceForLifeHttpSoap12Endpoint)
         Methods (1):
            method(xs:string args0, )  # 此处脱敏
         Types (1):
            Exception
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值