需求:实现基于cxf框架的webservice
主要内容
开发环境
调试工具
spring项目实现基于cxf框架的webservice
1、cxf jar包
org.apache.cxf
cxf-core
3.2.5
org.apache.cxf
cxf-rt-frontend-jaxws
3.2.5
org.apache.cxf
cxf-rt-transports-http
3.2.5
org.apache.cxf
cxf-rt-ws-security
3.2.5
2、spring.xml相关配置
约束命名空间不能少
3、web.xml配置
CXF
org.apache.cxf.transport.servlet.CXFServlet
1
CXF
/services/*
4、接口类编写:
package com.zy.api.publish.route;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* @author TSY
*
* @Date: 2018-09-10
*
*
*/
@WebService(targetNamespace = "com.serviceTargetName")
public interface jdWebService {
public String waterPredictMinute(@WebParam(name = "jsonObj") String json);
public String waterPredictHour(@WebParam(name = "jsonObj") String json);
public String warning(@WebParam(name = "jsonObj") String json);
public String dispatcher(@WebParam(name = "jsonObj") String json);
public String closeLoopDispatcher(@WebParam(name = "jsonObj") String json);
public String closeLoopWater(@WebParam(name = "jsonObj") String json);
}
/////////////////// webservice开发说明 //////////////////////
/*
* @ 1.这里的@Webservice(targetNamespace="")的作用类似与spring的Controller层中的Controller("/helloworld"),用于定位
* @ 2.在要发布的服务接口类开头加上@WebService 在接口的实现类开头也加上@WebService 若两个类不在同一个包中
* 则还要在实现类上用targetNamespace指明目标命名空间。命名空间的值和接口上的值一样。
*
*/
5、实现类编写
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.jws.WebService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zy.core.enums.TypeEnum;
import com.zy.core.utils.DateUtil;
import com.zy.core.utils.FastJsonUtil;
import com.zy.core.utils.JacksonUtil;
import com.zy.system.entity.DispatcherScheme;
import com.zy.system.entity.PumpInfo;
import com.zy.system.entity.TempReviceData;
import com.zy.system.entity.WorkFlow;
import com.zy.system.service.DispatcherSchemeService;
import com.zy.system.service.PumpInfoService;
import com.zy.system.service.TempReciveDataService;
import com.zy.system.service.WorkFlowService;
import com.zy.system.websocket.CloseLoopDispatcherWebSocket;
import com.zy.system.websocket.JinhaiWebSocket;
import com.zy.system.websocket.WHGWebSocket;
import com.zy.system.websocket.WaterDemandWebSocket;
import com.zy.system.websocket.WorkFlowWebSocket;
/**
* @author TSY
*
* @Date: 2018-09-11
*
*/
/*
* 由于实现类和接口不在同一个包中。所以要加上targetNamespace属性。
* 另外,这里的endpointInterface是实现类对应接口的全路径
*
*/
@WebService(targetNamespace="com.serviceTargetName",endpointInterface="com.zy.api.publish.route.jdWebService")
@Component("jdWebService")//spring注入用
public class jdWebServiceImpl implements jdWebService{
@Autowired
private XxxService xxxService;
//ip地址+端口+工程名+webxml配置路径+spring配置的路径+?wsdl
//url=localhost:8080/xxxx/services/jdWebService?wsdl
@Override
public String closeLoopDispatcher(String json) {
//业务处理
return "success";
}
@Override
public String closeLoopWater(String json) {
//业务处理
return "success";
}
@Override
public String waterPredictMinute(String json) {
return "success";
}
@Override
public String waterPredictHour(String json) {
return "success";
}
@Override
public String warning(String json) {
return "success";
}
@Override
public String dispatcher(String json) {
return "success";
}
}
/* 以下是webService 的注解说明
* @param endpointInterface指定接入点接口:接口必须存在
* @param targetNamespace service 命名空间,一般为域名倒写
* @param serviceName: 对外发布的服务名,指定 Web Service 的服务名
* 称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
*/
6、验证接口是否发布成功
在浏览器中输入访问地址,出现wsdl文档说明,发布成功啦
7、客户端调用
纯java 调用,不使用任何框架https://blog.csdn.net/chrisjingu/article/details/52385632
通过框架调用(略)
package com.zy.api.recieve.action;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @author TSY
*
* @Date: 2018-09-25
*
*/
public class GiveWaterDemandService {
public static final String address = "http://xxxxx.nat123.cc:18837/?wsdl";//外网测试用
//public static final String address = "http://192.168.137.25:8000/?wsdl";//内网正式用
public static String giveWaterDemandToJD(String waterdemand) {
String result=null;
InputStream is =null;
try {
//第一步:创建服务地址
URL url = new URL(address);
//第二步:打开一个通向服务地址的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//第三步:设置参数
//3.1发送方式设置:POST必须大写
connection.setRequestMethod("POST");
//3.2设置数据格式:content-type
connection.setRequestProperty("content-type", "text/xml;charset=utf-8");
//3.3设置输入输出,因为默认新创建的connection没有读写权限,
connection.setDoInput(true);
connection.setDoOutput(true);
//第四步:组织SOAP数据,发送请求
String soapXML = getXML(waterdemand);
//将信息以流的方式发送出去
OutputStream os = connection.getOutputStream();
os.write(soapXML.getBytes());
//第五步:接收服务端响应,打印
int responseCode = connection.getResponseCode();
if(200 == responseCode){//表示服务端响应成功
//获取当前连接请求返回的数据流
is = connection.getInputStream();
/*
* dom4j解析返回的流信息
*/
result = ParseSoapXML.result(is);
System.out.println("推送确认的需水量数据给交大结果++++++++++++++++++++++++"+result);
}
os.close();
}catch( IOException e) {
System.out.println("异常--推送确认的需水量数据给交大结果++++++++++++++++++++++++"+ e.getMessage());
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String getXML(String waterdemand){
String soapXML ="" +
" " +
"" +
"" +
""+
waterdemand
+ " " +
" " +
" " +
" "
;
return soapXML;
}
}
package com.zy.api.recieve.action;
import java.io.InputStream;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
*
* @author Administrator
*
* dom4j解析需水量webservice返回的流信息
*/
public class ParseSoapXML {
public static String result(InputStream xml){
try {
SAXReader reader = new SAXReader();
Document doucment = reader.read(xml);
Element root = doucment.getRootElement();
Element bo = root.element("Body");
Element response = bo.element("waterdemandResponse");
Element re = response.element("waterdemandResult");
System.out.println(re.getText());
return re.getText();
} catch (DocumentException e) {
e.printStackTrace();
}
return "false";
}
}
很简单,将整个cxf相关的xml文件块,复制一份即可
然后,编写相关的接口和实现类
1、导入security相关的jar包
2、配置xml文件
WS-Security拦截器,回掉函数需要注入到bean(
2、编写回调函数
package com.zy.api.publish.route;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.wss4j.common.ext.WSPasswordCallback;
/*
* 参考博客:https://blog.csdn.net/chrisjingu/article/details/52385632
*/
public class ServerPasswordCallback implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
if ("ThirdParty".equals(pc.getIdentifier())) {
pc.setPassword("egbIh87wmnHbFiKqad9M8NNtdql");
}
}
}
/*
* 使用UsernameToken方式验证,在server端的回调函数中WSPasswordCallback只能获得用户名(identifier),一般这里的逻辑是使用这个用户名到数据库中查询其密码,
* 然后再设置到password属性,WSS4J 会自动比较客户端传来的值和你设置的这个值。你可能会问为什么这里CXF 不把客户端提交的密码传入让我们在ServerPasswordCallbackHandler中比较呢?
* 这是因为客户端提交过来的密码在SOAP 消息中已经被加密为MD5 的字符串,如果我们要在回调方法中作比较,那么第一步要做的就是把服务端准备好的密码加密为MD5 字符串,
* 由于MD5 算法参数不同结果也会有差别,另外,这样的工作CXF 替我们完成了.
*/
/*
* 使用ws-security进行安全验证,概括来说分两步:
一、创建一个验证类,如本实例的ServerPasswordCallback.class
二、配置WSS4JInInterceptor拦截器,如applicationContext-cxf.xml所示。
三、将applicationContext-cxf.xml配置到web.xml的contextConfigLocation属性中
*该类实现ws-security的CallbackHandler 接口,并重写它的handle方法。该类就是身份验证的主要类,
*当客户端传过的用户名中为“huwei“时,该方法会将指定的密码告知ws-security的WSPasswordCallback 类,
*并让它后期去和客户端的密码进行验证,通过就放行,否则打回。
*在applicationContext-cxf.xml中该类会作为WSS4JInInterceptor拦截器的回调函数属性,进行配置。
*/
3、客户端测试(SoapUI)
【浏览器、soapui都可以进行查看,但是无法进行正确调用,需要设置用户名和密码】
提示如下错误:
需要设置用户名和密码;
比如:soapui直接再header里面加上如下信息,即可
用其他框架也可以设置用户名和密码进行请求
小技巧:http请求时,xml文件可以直接copy 工具SoapUI生成的xml文件
ThirdParty
egbIh87wmnHbFiKqad9M8NNtdql