项目需要,发布webservice服务端,向客户端传递复杂对象。对象包含了list<>,反复研究了一下,发现webservice不能够直接传递list对象,必须转换成对象数组才能传递。javaBean:
package com.wensi.service.server; import java.io.Serializable; public class LoginResponse implements Serializable{ private boolean flag;//操作员登录验证是否通过:true 验证通过 false 验证失败 private OperatorBean operator;//登录成功后返回的操作员信息,登录失败返回null private AuthorityBean[] authorityArray;//登录成功后返回操作员的权限,登录失败返回null private String checkNum;//校验码 public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public OperatorBean getOperator() { return operator; } public void setOperator(OperatorBean operator) { this.operator = operator; } public AuthorityBean[] getAuthorityArray() { return authorityArray; } public void setAuthorityArray(AuthorityBean[] authorityArray) { this.authorityArray = authorityArray; } public String getCheckNum() { return checkNum; } public void setCheckNum(String checkNum) { this.checkNum = checkNum; } }
这是一个复杂的自定义对象,该对象嵌套了基本类型、自定义对象以及自定义对象数组。其余的对象我就不细说了,来看一下,axis1.4用来发布服务的wsdd文件:
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <handler type="java:org.apache.axis.handlers.http.URLMapper" name="URLMapper"/> <handler type="java:com.wensi.service.server.BSHeaderHandler" name="BSHeaderHandler"/> <service name="ZNWService" provider="java:RPC"> <requestFlow> <handler type="BSHeaderHandler"/> </requestFlow> <parameter name="className" value="com.wensi.service.server.ZNWService"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="session"/> <!-- 这里定义了方法的参数以及返回值 --> <operation name="login" returnType="ns:LoginResponse"> <parameter name="userName" type="tns:string"/> <parameter name="password" type="tns:string"/> </operation> <!-- 这里定义了自定义对象的映射 --> <typeMapping qname="ns:OperatorBean" xmlns:ns="ZNWService" type="java:com.wensi.service.server.OperatorBean" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" /> <typeMapping qname="ns:AuthorityBean" xmlns:ns="ZNWService" type="java:com.wensi.service.server.AuthorityBean" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" /> <arrayMapping qname="ns:ArrayOfAuthorityBean" xmlns:ns="ZNWService" type="java:com.wensi.service.server.AuthorityBean[]" innerType="ns:AuthorityBean" xmlns:ns2="ZNWService" deserializer="org.apache.axis.encoding.ser.ArrayDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" serializer="org.apache.axis.encoding.ser.ArraySerializerFactory" /> <typeMapping qname="ns:LoginResponse" xmlns:ns="ZNWService" type="java:com.wensi.service.server.LoginResponse" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" /> </service> <transport name="http"> <requestFlow> <handler type="URLMapper"/> </requestFlow> </transport> </deployment>
注意:<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
</requestFlow>
</transport>
如果没有该配置,将无法在浏览器中显示wsdl详细信息。
<handler type="java:com.wensi.service.server.BSHeaderHandler" name="BSHeaderHandler"/>
和
<requestFlow>
<handler type="BSHeaderHandler"/>
</requestFlow>
是实现一个相当于拦截器的功能,客户端在调用服务端的接口之前,必须经过这个方法的处理。
在这里,我写了一个权限验证的拦截器,代码如下:
package com.wensi.service.server; import org.apache.axis.AxisFault; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.handlers.BasicHandler; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeader; import org.apache.axis.message.SOAPHeaderElement; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.message.MessageElement; import com.wensi.common.Util; import javax.xml.namespace.QName; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.Map; @SuppressWarnings("unchecked") public class BSHeaderHandler extends BasicHandler { private static final long serialVersionUID = 1L; private String operNum;//操作员编号 private String checkNum;//校验码 private String time;//访问接口的时间 String endpoint = ServiceConstant.endpoint;//webservice的具体路径 Map map = ServiceConstant.checkMap;//包含校验码信息的map // 在执行service前先处理handler // invoke()会被自动调用,而且 SOAP 信息可以由 msgContext 取得 public void invoke(MessageContext msgContext) throws AxisFault { boolean processedHeader = false; try { // 取得 Request 的 SOAP 信息 Message msg = msgContext.getRequestMessage(); SOAPEnvelope envelope = msg.getSOAPEnvelope(); SOAPHeader header = envelope.getHeader(); Iterator it = header.examineAllHeaderElements(); SOAPHeaderElement hel; while (it.hasNext()) { hel = (SOAPHeaderElement) it.next(); String headerName = hel.getNodeName(); if (headerName.equals("cp:MessageHeader")) { // 对于 mustUnderstand 设为 true 的 Header,必须 // 利用下列的方式把它设为"已经处理",否则 service // 会回传 Did not understand "MustUnderstand" hel.setProcessed(true); checkUser(hel); processedHeader = true; } } } catch (SOAPException e) { throw new AxisFault("无法处理 SOAP Header.", e); } if (!processedHeader) { throw new AxisFault("接收 SOAP Header 失败"); } } private void checkUser(SOAPHeaderElement hel) throws AxisFault { String current = Util.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss");//当前时间字符串 MessageElement element1 = hel.getChildElement(new QName( endpoint, "isLogin")); MessageElement element2 = hel.getChildElement(new QName( endpoint, "operNum")); MessageElement element3 = hel.getChildElement(new QName( endpoint, "checkNum")); MessageElement element4 = hel.getChildElement(new QName( endpoint, "time")); //用户访问的不是登录接口,则进行校验码验证 if(null == element1 && null != element2 && null != element3 && null != element4){ operNum = element2.getValue(); checkNum = element3.getValue(); time = element4.getValue(); Boolean flag1 = false; if(map.keySet().contains(operNum)){ CheckNumBean check = (CheckNumBean) map.get(operNum); System.out.println(check.getCheckNum()); if(check.getCheckNum().equals(checkNum)){ flag1 = true; } } Boolean flag2 = false; if(flag1){ //计算服务器时间与soapHeader时间戳之间的差值 Long between = countTime(time,current); //时差在一个小时之内,算正常 if(Math.abs(between) < 3600L){ flag2 = true; } } if(!(flag1 && flag2)){ throw new AxisFault("校验失败"); } //更新map内信息 CheckNumBean check = (CheckNumBean) map.get(operNum); check.setLastTime(check.getCurrentTime()); check.setCurrentTime(current); } else if(null == element1 && (null == element2 || null == element3 || null == element4)){ throw new AxisFault("校验失败"); } } //计算时间差 private Long countTime(String str1, String str2){ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date begin = null; Date end = null; try { begin = df.parse(str1); end = df.parse(str2); } catch (ParseException e) { e.printStackTrace(); } long between=(end.getTime()-begin.getTime())/1000;//除以1000是为了转换成秒 return between; } }
由于客户端是手持机,而这边的客户端是java ssh架构,所以存储用户信息,正能依靠内存来完成。
客户端调用的时候,需要向soapHeader放验证信息。
客户端代码如下:
package com.wensi.service.client; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.Date; import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceException; import javax.xml.rpc.encoding.XMLType; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.ser.ArrayDeserializerFactory; import org.apache.axis.encoding.ser.ArraySerializerFactory; import org.apache.axis.encoding.ser.BeanDeserializerFactory; import org.apache.axis.encoding.ser.BeanSerializerFactory; import org.apache.axis.message.SOAPHeaderElement; import com.wensi.common.Util; import com.wensi.service.server.AccountCustomerBean; import com.wensi.service.server.AccountFlag; import com.wensi.service.server.AccountResponse; import com.wensi.service.server.AuthorityBean; import com.wensi.service.server.LoginResponse; import com.wensi.service.server.OperatorBean; import com.wensi.service.server.ServiceConstant; public class ZNWServiceClient { String endpoint = ServiceConstant.endpoint;//webservice的具体路径 Service service = new Service();//创建service实例 public LoginResponse login(String userName,String password) throws Exception{ SOAPHeaderElement cpHeader = new SOAPHeaderElement(endpoint,"MessageHeader"); cpHeader.setPrefix("cp"); cpHeader.setMustUnderstand(true); SOAPElement ele = null; ele = cpHeader.addChildElement("isLogin"); ele.addTextNode("true"); //创建call实例 Call call = (Call) service.createCall(); //将webservice的服务路径加入到call实例中,并为call设置服务的位置 URL url = new URL(endpoint); call.setTargetEndpointAddress(url); //调用webservice 的方法 call.setOperationName("login"); //序列化对象 QName qn1 = new QName("urn:ZNWService","OperatorBean"); call.registerTypeMapping(OperatorBean.class, qn1, new BeanSerializerFactory(OperatorBean.class,qn1), new BeanDeserializerFactory(OperatorBean.class,qn1)); QName qn2 = new QName("urn:ZNWService","AuthorityBean"); call.registerTypeMapping(AuthorityBean.class, qn2, new BeanSerializerFactory(AuthorityBean.class,qn2), new BeanDeserializerFactory(AuthorityBean.class,qn2)); QName qn3 = new QName("urn:ZNWService","ArrayOfAuthorityBean"); call.registerTypeMapping(AuthorityBean[].class, qn3,new ArraySerializerFactory(),new ArrayDeserializerFactory()); QName qn4 = new QName("urn:ZNWService","LoginResponse"); call.registerTypeMapping(LoginResponse.class, qn4, new BeanSerializerFactory(LoginResponse.class,qn4), new BeanDeserializerFactory(LoginResponse.class,qn4)); call.addParameter("userName", org.apache.axis.Constants.XSD_STRING,ParameterMode.IN); call.addParameter("password", org.apache.axis.Constants.XSD_STRING,ParameterMode.IN); call.setReturnType(qn1, LoginResponse.class); call.addHeader(cpHeader); LoginResponse loginRes = (LoginResponse) call.invoke(new Object[]{userName,password}); return loginRes; } }