java_webservice_jaxws_服务端_客户端

之前项目需要与客户方进行接口交接,选择了webservice接口。这里记录下如何创建服务端,客户端。

  • 服务端
    关于发布webservice,其实主要使用了javax.xml.ws.Endpoint类提供的静态方法publish进行发布,但形式可以使用多种方式,比如:
    ①通过一个类的main方法+publish方法

    ②BS项目中的使用Servlet3.0提供的@WebListener注解将实现了ServletContextListener接口的WebServicePublishListener类标注为一个Listener

    ③通过servlet的init()方法,且在注解中@webService设置loadOnStartup=0:@WebServlet(value="",loadOnStartup=0)意思就是启动服务器是首先启动。

在项目中我主要采用的是第三种方式,通过init()在初始化servlet的时候就发布webService接口。
定义一个主类,里面是对外公布的public接口方法:

package test;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.xml.ws.Endpoint;


@WebServlet(value="",loadOnStartup=0)
public class publishWS extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public publishWS() {
          super();
        // TODO Auto-generated constructor stub
    }
    
    public void init() throws ServletException {
        //自定义服务器端接口地址,如果是本地的话,ip需要自己查,后面的路径是自己
        //来定义,这里最好做成xml配置文件来取,部署的时候方便
        String Address = "http://192.168.103.145:9527/Service/Contract/WebService";
        //publish第二个参数是我们的实现类
        Endpoint.publish(Address , new mainInterface());
        System.out.println("webService接口发布成功!");
	 }
}

主要对外接口方法,需要注解@WebService

package test;

import javax.jws.WebService;

@WebService
public class mainInterface {

	public String func1(String xx...) {
		return 
	}
	

	public String func2(String xx ...) {
	    return 
	}
	
	public String func3(String xx...) {
		return 
	}
}
  • 更新20190121(今天客户那边要求加入安全验证性,发现上面那个例子不好用,上面虽然能调用但是其他事情不友好,下面更改了一版)
    创建一个接口 mainInterface.class
package test;

import javax.jws.HandlerChain;
import javax.jws.WebService;

import org.json.JSONException;

@WebService
@HandlerChain(file="handler-chain.xml")
public interface mainInterface {
	public String func1;
	
	public String func2;
	
	public String func3;
}

在创建一个类切实现这个接口 mainImplement.class

package test;

import javax.jws.HandlerChain;
import javax.jws.WebService;

import org.json.JSONException;

@WebService
@HandlerChain(file="handler-chain.xml")
public class mainImplatement implements mainInterface {

	@Override
	public String func1() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String func2() {
		// TODO Auto-generated method stub
	}

	@Override
	public String func3() {
		// TODO Auto-generated method stub
	}

}

  • 如何在服务器端添加安全验证?
    利用注解@HandlerChain(file=“handler-chain.xml”),且在classpath路径下(一般在WEB-INF下的classes里面),创建handle-chain.xml
<?xml version="1.0" encoding="UTF-8"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<javaee:handler-chain>
		<javaee:handler>
			<javaee:handler-class>test.validateAuthHeader</javaee:handler-class>
		</javaee:handler>
	</javaee:handler-chain>
</javaee:handler-chains>

validateAuthHeader.class

package test;

import java.util.Iterator;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class validateAuthHeader implements SOAPHandler<SOAPMessageContext> {
	@Override
	public boolean handleFault(SOAPMessageContext context) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void close(MessageContext context) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Set<QName> getHeaders() {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public boolean handleMessage(SOAPMessageContext context) {
		// TODO Auto-generated method stub
		
		//判断消息是请求还是响应
		Boolean output = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
		
		Boolean result = false;
		
		SOAPMessage message = context.getMessage();
		
		if(!output) {
			result = validateSuccess(message);
			if(!result) {
				validateFail(message);
			}
		}
		
		//System.out.println(output ? "服务端响应:" : "服务端接收:");
		try {
			message.writeTo(System.out);			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("\r\n");
 
		return result;
	}

	
	
	//成功验证
	private boolean validateSuccess(SOAPMessage message) {
		boolean result = false;
		try {
			SOAPEnvelope envelop = message.getSOAPPart().getEnvelope();
			SOAPHeader header = envelop.getHeader();
			
			if(header != null){
				Iterator iterator = header.getChildElements(new QName("http://www.tmp.com/auth", "auth"));
				SOAPElement auth = null;
				
				if(iterator.hasNext()){
					//获取auth
					auth = (SOAPElement)iterator.next();
					
					//获取username
					Iterator it = auth.getChildElements(new QName("http://www.tmp.com/auth", "username"));
					SOAPElement username = null;
					if(it.hasNext()){
						username = (SOAPElement)it.next();
					}
					
					//获取password
					it = auth.getChildElements(new QName("http://www.tmp.com/auth", "password"));
					SOAPElement password = null;
					if(it.hasNext()){
						password = (SOAPElement)it.next();
					}
					
					//判断username和password是否符合要求
					if(username != null && password != null && "admin".equals(username.getValue()) && "admin".equals(password.getValue())){
						result = true;
					}
				}
			}
			
		} catch (SOAPException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	//失败验证
	private void validateFail(SOAPMessage message) {
		try {
			SOAPEnvelope envelop = message.getSOAPPart().getEnvelope();
 
			envelop.getHeader().detachNode();
			envelop.addHeader();
 
			envelop.getBody().detachNode();
			SOAPBody body = envelop.addBody();
 
			SOAPFault fault = body.getFault();
 
			if (fault == null) {
				fault = body.addFault();
			}
 
			fault.setFaultString("用户名密码验证失败 请重新输入");
 
			message.saveChanges();
		} catch (SOAPException e) {
			e.printStackTrace();
		}
	}
}

添加上服务器端的安全验证后,那么客户端的soap请求头中必须包含如下:

	<soapenv:Header>
		<auth xmlns="http://www.tmp.com/auth">
			<username>admin</username>
			<password>admin</password>
		</auth>
	</soapenv:Header>

在这里插入图片描述
在浏览器中输入url地址:
在这里插入图片描述
查看接口的报文格式:
在这里插入图片描述
这里我使用的soapUI这款软件,加载带wsdl的报文地址,可以看到接口的方法及它request请求的soap格式,可以用来测试接口:
在这里插入图片描述

  • 客户端
    一般对方接口人员也会给你个webService地址,此时我们可以查看带?wsdl的url来查看它的报文xml结构,里面会显示对方公布的方法。生产客户端代码的方式有很多种,比如jdk自带的wsimport,apache cxf等,项目里我选择了jdk这种方式:
wsimport -d '指定生成代码的路径' -keep '对方的webservice地址+?wsdl'

个别参数含义:
-d : 表示输出的目录,目录必须事先存在,否则导出失败
-keep : 表示生成客户端执行类的源代码
-p : 定义客户端生成类的包名称
-s : 指定客户端执行类的源文件存放目录
-b : 指定jaxws/jaxb绑定文件或额外的schemas
-verbose : verbose表示详细信息
-extension : 使用扩展来支持SOAP1.2

这里再记录下,当时对方的接口需要权限认证,就是要求用户名,密码,但是我生成的代码里面没有设置这类的方法,后面查询了很多资料,需要重写指定方法:

//这里的xxx 类是根据wsdl自动解析生成的
xxx xx= new xxx();
		xx.setHandlerResolver(new HandlerResolver() {
			@Override
			public List<Handler> getHandlerChain(PortInfo arg0) {
				List<Handler> handlerList = new ArrayList<Handler>();
				// 添加认证信息
				handlerList.add(new setHeader());
				return handlerList;
			}
		});

setHeader.class

package test;

import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class setHeader implements SOAPHandler<SOAPMessageContext> {

	@Override
	public void close(MessageContext arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean handleFault(SOAPMessageContext arg0) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean handleMessage(SOAPMessageContext ctx) {
		// TODO Auto-generated method stub
		
		// 出站,即客户端发出请求前,添加表头信息
		Boolean request_p = (Boolean) ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
		if (request_p) {
			try {
				SOAPMessage msg = ctx.getMessage();
				SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
				SOAPHeader hdr = env.getHeader();
				if (hdr == null)
					hdr = env.addHeader();

				// 添加认证信息头
				QName name = new QName(
						"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
						"Security", "wsse");
				SOAPHeaderElement header = hdr.addHeaderElement(name);
				SOAPElement UsernameToken = header.addChildElement("UsernameToken", "wsse");
				SOAPElement userElement = UsernameToken.addChildElement("Username", "wsse");
				userElement.addTextNode("你的用户名");
				SOAPElement passElement = UsernameToken.addChildElement("Password", "wsse");
				passElement.addTextNode("你的密码");

				msg.saveChanges();

				// 把SOAP消息输出到System.out,即控制台
				//msg.writeTo(System.out);

				return true;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	@Override
	public Set<QName> getHeaders() {
		// TODO Auto-generated method stub
		return null;
	}
}

其余的就是根据实际业务来进行编码。对于webservcie还有很多方法可以实现,这里我只是简单记录下自己在项目中使用到的几点。但是我感觉这种根据别人的wsdl地址来生成代码很受限制,如果对方更改了webservice地址或者接口 方法我都需要重新切生成一遍,可能是我不清楚或者还没找到合适的方式来适应这种,都是这种笨办法重新生成。

参考资料:
初识webservice

添加安全认证

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值