【概述】
本文基于webservice技术实现平台架构, 此架构用于提供平台服务,服务可以用于客户的对接以达到服务客户的目的,并且保证服务过程中的通讯数据是安全的, 类似于 支付宝的支付服务,商家可以申请相应的帐号和私密信息,用于完成支付操作并且保障信息的不可篡改, 但支付宝是通过RSA来校验数据的安全性,而本文是使用md5来校验数据, 客户端与服务端的通讯使用webservice, 以https形式进行交互, 本文假定你已经对axis的webservice的机制和使用有一定的了解
【流程】
1. 客户向平台服务申请AppID(axcpud123)和AppKeySecret(124jfj3ff3AjifLLefl)
2. 客户准备数据, 数据内容为 telPhone=15261873121, userId=000001, idCert=32132219871234232
AppID+ & + (用户数据内容按照key名称从小到大排序) + & + AppKeySecret
对上面的数据进行MD5进行加密码,生成密文 sign
3. 以axis webservice向服务端发送服务, 服务方法名 为serverMethod
url_wsdl: https://域名/工程名/webservice名称?wsdl
@Override public String ysSignUrl(){ StringBuilder sb=new StringBuilder(); try{ String time=getTimeStr(); TestDTO data = new TestDTO(); data .setAppId(AppId); data .setTime(time); data .setTelPhone(phone); data .setUserId(userId); data .setIdCert(idCert); String j=JSON.toJSONString(data,true); JSONObject json=JSONObject.fromObject(j); String sortJson=commUtil.getSortStr(json,Constants.REGX); sb.append(sortJson).append(Constants.REGX).append(data.getAppSecretKey()); String sign=Md5Util.getMd5(sb.toString()); sb.setLength(0); sb.append(data.getYsUrl()).append(url_wsdl); Service service = new Service(); Call call=(Call)service.createCall(); call.setTargetEndpointAddress(new java.net.URL(sb.toString())); call.setOperationName(new QName("http://wsdl.com/", "serverMethod")); call.addParameter("appId", XMLType.XSD_STRING, ParameterMode.IN); call.addParameter("time", XMLType.XSD_STRING, ParameterMode.IN); call.addParameter("sign", XMLType.XSD_STRING, ParameterMode.IN); call.addParameter("telPhone", XMLType.XSD_STRING, ParameterMode.IN); call.addParameter("userId", XMLType.XSD_STRING, ParameterMode.IN); call.addParameter("idCert", XMLType.XSD_STRING, ParameterMode.IN); call.setReturnType(XMLType.XSD_STRING); String result = call.invoke(new Object[] { data.getAppId(),time, sign, data.getTelPhone(), data.getUserId(), data.getIdCert() }).toString(); }catch(ServiceException se ){ }catch(MalformedURLException me){ }catch(RemoteException re){ }catch(Exception e){ } return result; }
4. webservice服务端接收到数据后,取出数据中的 AppID(axcpud123)
telPhone=15261873121, userId=000001, idCert=32132219871234232, sign值
根据AppID到平台数据库中查找AppKeySecret(124jfj3ff3AjifLLefl), 然后对AppId, telPhone, userId, idCert,
AppKeySecret进行MD5加密 得到signNew, 然后比较sign与signNew是否相同,如果相同,则认为客户端与服务端的通讯数据未被修改
小知道点:
1. FastJson对json数据的key值按照a-z排序
public static String getSortStr(JSONObject json,String regx){ StringBuilder sortStr=new StringBuilder(); Iterator<String> iteratorKeys = json.keys(); SortedMap map = new TreeMap(); while (iteratorKeys.hasNext()) { String key = iteratorKeys.next().toString(); String vlaue = json.optString(key); map.put(key, vlaue); } Set set = map.entrySet(); Iterator i = set.iterator(); while(i.hasNext()){ Map.Entry me = (Map.Entry)i.next(); String value=me.getValue().toString(); if(!value.isEmpty()){ sortStr.append(regx).append(value); } } return sortStr.substring(1,sortStr.length()); }
2. MD5加密
.生成字符串MD5
.生成字节MD5
.生成文件的MD5值
/** * <p>Title: MD5Util.java</p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2018</p> * <p>Company: jsjn</p> * @author zhengwei * @date 2018年4月28日 * @version 1.0 */ package com.jsjn.slarms.extSysDock.common.util; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * <p>Title: MD5Util</p> * <p>Description: MD5算法</p> * @author zhengwei * @date 2018年4月28日 */ public class Md5Util { private static final String MD5="MD5"; private static final String UTF8="UTF-8"; private static final Integer LENGTH=1024; /** * * <p>Title: getMd5</p> * <p>Description: 获取MD5运算之后的值</p> * @param str * @return */ public final static String getMd5(String str) { MessageDigest mdInst = null; byte[] md=null; try { mdInst = MessageDigest.getInstance(MD5); /** * 使用指定的字节更新摘要 */ mdInst.update(str.getBytes(UTF8)); /** * 获得密文 */ md = mdInst.digest(); } catch (Exception e) { e.printStackTrace(); } return byteArrToHexStr(md); } private static String byteArrToHexStr(byte[] arrB) { int iLen = arrB.length; /** * 每个byte(8位)用两个(16进制)字符才能表示,所以字符串的长度是数组长度的两倍 */ StringBuffer sb = new StringBuffer(iLen * 2); for (int i = 0; i < iLen; i++) { int intTmp = arrB[i]; /** * 把负数转换为正数 */ while (intTmp < 0) { intTmp = intTmp + 256; } /** * 小于0F的数需要在前面补0 */ if (intTmp < 16) { sb.append("0"); } sb.append(Integer.toString(intTmp, 16)); } return sb.toString(); } /** * * <p>Title: getFileInputStreamMD5</p> * <p>Description: 计算文件流MD5值</p> * @param in * @return * @throws NoSuchAlgorithmException * @throws IOException */ public static String getFileInputStreamMD5(InputStream in) throws NoSuchAlgorithmException, IOException { MessageDigest digest = null; byte buffer[] = new byte[1024]; int len; digest = MessageDigest.getInstance(MD5); while ((len = in.read(buffer, 0, LENGTH)) != -1) { digest.update(buffer, 0, len); } in.close(); BigInteger bigInt = new BigInteger(1, digest.digest()); return bigInt.toString(16); } }