一、Soap消息
Web Services通信的内容。如下
<!-- 根元素: Envelope -->
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<!-- Header:不强制出现的元素,可用于携带一些wsdl定义之外的信息,如用户名信息等 -->
<soap:Header>
<userInfo>
<userName>CxfName</userName>
<userPass>CxfPass</userPass>
</userInfo>
</soap:Header>
<!-- Body:Soap的消息体,在正确调用的情况下符合WSDL文档定义的格式,调用错误则为soap:Fault子元素 -->
<soap:Body>
<ns2:sayHello xmlns:ns2="http://ws.cxf.xilen.com/">
<arg0>Admin</arg0>
</ns2:sayHello>
</soap:Body>
</soap:Envelope>
二、添加拦截器
1、服务端
import java.util.List;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;
import com.xilen.cxf.ws.UserInfoWs;
import com.xilen.cxf.ws.impl.UserInfoWsImpl;
public class ServerMain {
public static void main(String[] args) {
UserInfoWs ui = new UserInfoWsImpl();
// 得到javax.xml.ws.Endpoint.publish()发布方法的org.apache.cxf.jaxws.EndpointImpl类型返回值
EndpointImpl ep = (EndpointImpl) Endpoint.publish("http://192.168.1.100/cxf", ui);
// 通过这个返回值的getInInterceptors()和getOutInterceptors()方法得到现有的对应拦截器集合
List<Interceptor<? extends Message>> interceptorIn = ep.getInInterceptors();
List<Interceptor<? extends Message>> interceptorOut = ep.getOutInterceptors();
// 通过这个集合的添加方法添加拦截器 LoggingInterceptor为Cxf自带拦截器
interceptorIn.add(new LoggingInInterceptor());
interceptorOut.add(new LoggingOutInterceptor());
}
}
2、客户端
客户端此时使用了Cxf相关的对象,需要添加Cxf的Jar
public class ClientMain {
public static void main(String[] args) {
//在生成的Java类中继承javax.xml.ws.Service的类可作为工厂来使用
UserInfoWsImpl factory = new UserInfoWsImpl();
//通过工厂类实例来获取远程Web Service对象的代理
UserInfoWs uiws = factory.getUserInfoWsImplPort();
//以远程代理对象为参数调用ClientProxy的getClient()方法获取Client对象
Client client = ClientProxy.getClient(uiws);
//获取Client对象的输入输出拦截器集合并添加新的拦截器
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
//调用代理对象的方法实现远程调用
System.out.println(uiws.sayHello("Admin"));
}
}
三、自定义拦截器
1、说明以调用Web Services时需要验证用户为例说明。服务器端需要添加In拦截器验证是否有用户信息并符合要求,而客户端则需要添加Out拦截器把用户信息加入到Soap消息中。
2、服务器端
(1)定义服务器端拦截器
import java.util.List;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* 自定义拦截器继承Interceptor接口,可通过继承AbstractPhaseInterceptor<Message>来实现(Message使用SoapMessage的实现)
* 通过PhaseInterceptor拦截器可以指定这个拦截器在服务的哪个阶段起作用,具体阶段在实现类的构造器中传入
*/
public class ServerAuthInInterceptor extends AbstractPhaseInterceptor<SoapMessage>{
/**
* AbstractPhaseInterceptor没有无参构造器,需要在子类中定义构造器并显示的调用其有参数的构造器
*/
public ServerAuthInInterceptor(){
/*
* 显示调用父类的构造器。参数通过Phase类的常量指定,即这个拦截器起作用的阶段
* Phase.PRE_INVOKE: 调用之前起作用
*/
super(Phase.PRE_INVOKE);
}
/**
* 重写AbstractPhaseInterceptor的handleMessage()方法 定义拦截业务的逻辑
* 参数message就是被拦截到的soap消息
* 对符合逻辑的请求Cxf不用多余的操作,只需要在不符合业务逻辑的请求Throw Fault
*/
public void handleMessage(SoapMessage message) throws Fault {
/*
* soap body是固定的, 其他信息只能放在Header中
* 通过SoapMessage对象的getHeader()方法得到Header的集合
*/
List<Header> headers = message.getHeaders();
if (headers.isEmpty()) {
throw new Fault(new IllegalArgumentException("没有携带额外的信息!"));
}
// 假设要求第一个Header就携带用户信息
Header header = headers.get(0);
// 得到的是DOM的元素
Element element = (Element) header.getObject();
// DOM解析
NodeList nodeUserName = element.getElementsByTagName("userName");
NodeList nodeUserPass = element.getElementsByTagName("userPass");
if(nodeUserName.getLength() != 1 || nodeUserPass.getLength() != 1){
throw new Fault(new IllegalArgumentException("没有携带用户信息或者不符合要求!"));
}
// 得到具体的用户名和密码并验证
String userName = nodeUserName.item(0).getTextContent();
String userPass = nodeUserPass.item(0).getTextContent();
if(!userName.equals("CxfName") || !userPass.equals("CxfPass")){
throw new Fault(new IllegalArgumentException("用户名或者密码不正确!"));
}
// 如果验证一路通过,则请求自动继续往下
}
}
(2)添加服务器端拦截器
public class ServerMain {
public static void main(String[] args) {
UserInfoWs ui = new UserInfoWsImpl();
// 得到javax.xml.ws.Endpoint.publish()发布方法的org.apache.cxf.jaxws.EndpointImpl类型返回值
EndpointImpl ep = (EndpointImpl) Endpoint.publish("http://192.168.1.100/cxf", ui);
// 通过这个返回值的getInInterceptors()和getOutInterceptors()方法得到现有的对应拦截器集合
List<Interceptor<? extends Message>> interceptorIn = ep.getInInterceptors();
List<Interceptor<? extends Message>> interceptorOut = ep.getOutInterceptors();
// 通过这个集合的添加方法添加拦截器
interceptorIn.add(new LoggingInInterceptor());
interceptorOut.add(new LoggingOutInterceptor());
//添加自定义拦截器
interceptorIn.add(new ServerAuthInInterceptor());
}
}
3、客户端
(1)定义客户端拦截器
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class ClientAuthOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String name;
private String pwd;
/**
* 除了调用父类构造方法之外,也把用户信息一并添加
*/
public ClientAuthOutInterceptor(String name, String pwd) {
// 在消息发送之前调用 此时Soap消息已经生成
super(Phase.PREPARE_SEND);
this.name = name;
this.pwd = pwd;
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
List<Header> headers = message.getHeaders();
// DOM编程
Document doc = DOMUtils.createDocument();
Element element = doc.createElement("userInfo");
// 封装用户信息
Element nameEle = doc.createElement("userName");
nameEle.setTextContent(name);
Element passEle = doc.createElement("userPass");
passEle.setTextContent(pwd);
element.appendChild(nameEle);
element.appendChild(passEle);
// 把element元素封装成Header并添加到Headers中 QName即命名空间
Header header = new Header(new QName("xilen"), element);
headers.add(header);
}
}
(2)添加客户端拦截器
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import com.xilen.cxf.ws.UserInfoWs;
import com.xilen.cxf.ws.impl.UserInfoWsImpl;
import com.xilen.inter.ClientAuthOutInterceptor;
public class ClientMain {
public static void main(String[] args) {
//在生成的Java类中继承javax.xml.ws.Service的类可作为工厂来使用
UserInfoWsImpl factory = new UserInfoWsImpl();
//通过工厂类实例来获取远程Web Service对象的代理
UserInfoWs uiws = factory.getUserInfoWsImplPort();
//以远程代理对象为参数调用ClientProxy的getClient()方法获取Client对象
Client client = ClientProxy.getClient(uiws);
//获取Client对象的输入输出拦截器集合并添加新的拦截器
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
//自定义用户认证拦截器,存入用户名密码信息
client.getOutInterceptors().add(new ClientAuthOutInterceptor("CxfName","CxfPass"));
//调用代理对象的方法实现远程调用
System.out.println(uiws.sayHello("Admin"));
}
}
4、调用结果(1)在用户信息输入正确的情况下,服务端顺利执行了相应的方法并返回
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:sayHelloResponse xmlns:ns2="http://ws.cxf.xilen.com/">
<return>Admin, Hello!</return>
</ns2:sayHelloResponse>
</soap:Body>
</soap:Envelope>
(2)在用户信息输入错误的情况下,服务器端只返回了错误调用的信息
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>用户名或者密码不正确!</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
客户端和服务端的工程源码已打包上传优快云,地址:http://download.youkuaiyun.com/detail/u013379717/7216357