所有接口均使用webservice方式提供,遵守soap協議,接口輸入輸出通過xml格式的string進行調用。通過post方式提交XML報文數據到接口,我司系統接收並返回XML報文數據,完成報文數據交換;我司是通過Axis2的形式實現的webservice服務.
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();
}
}
}
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);
}
}
<?xml version="1.0" encoding="GBK"?>
<node>
{a}
</node>
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) # 對返回數據進行解密
對方公司給出的KEY可能是任意位數的, 但java的DES加密方法可以使用這個KEY進行數據加解密, 但python的pyDes文檔要求KEY是8位的,怎麼辦?
答: 通過查看java的DES文檔, 發現也是截取了前8位, 因此在python中key = key[:8]
就可以解決問題.
我也是第一次調用webservice這類接口, 一臉懵逼, 試著去請求一些公用的webservice接口, 比如天氣預報類的, 手機號碼歸屬地類的,先確認自己的調用方法是沒問題的.再下一步.
對方文檔有N多個接口的對接方法, 但其實只有幾個是有權限的, 結果需求不同步, 在沒有權限的接口上聯調了好久, 最後才發現沒有權限請求,哭死了. 還好, 總算對接OK了.
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