1、CXF框架的深入使用,CXF的拦截器,为什么设计CXF拦截器?
答:为了在webservice请求过程中,能动态操作请求和响应数据,,CXF设计了拦截器。
2、CXF的拦截器分类:
1)、按所处的位置分:服务器端拦截器,客户端拦截器。
2)、按消息的方向分:入拦截器,出拦截器。
3)、按定义者分:系统拦截器,自定义拦截器。
3、使用拦截器就可以不适用tcp/ip监控的工具了,因为此监控工具还需要将wsdl文件下载到本地,然后修改端口才能进行监控。
将apache-cxf-2.5.9\lib里面的包导入到项目中,然后编写SEI(Service Endpoint Interface),SEI在webservice中称为portType,在java中就是普通接口。
1 package com.bie.webservice.sei; 2 3 import javax.jws.WebMethod; 4 import javax.jws.WebService; 5 6 /** 7 * 8 * @author 9 * 编写SEI(Service Endpoint Interface),SEI在webservice中称为portType,在java中就是普通接口 。 10 * 11 * 1、第一步,开发服务器端,Web Service编码。 12 * –@WebService(SEI和SEI的实现类),该注解用来定义SEI和SEI的实现类。 13 * –@WebMethod(SEI中的所有方法),该注解用来定义SEI里面的方法。 14 * 2、第二步,发布Web Service,–Endpoint(终端, 发布webservice)。 15 */ 16 @WebService 17 public interface HelloWebServiceSEI { 18 19 @WebMethod 20 public String sayHello(String name); 21 22 }
然后编写SEI实现类,如下所示:
1 package com.bie.webservice.sei.impl; 2 3 import javax.jws.WebService; 4 5 import com.bie.webservice.sei.HelloWebServiceSEI; 6 7 /** 8 * 9 * @author 10 * 11 * 1、SEI实现类 12 * 13 * 14 */ 15 @WebService // SEI实现类也要使用此注解 16 public class HelloWebServiceSEIImpl implements HelloWebServiceSEI { 17 18 @Override 19 public String sayHello(String name) { 20 System.out.println("Service server sayHello() : " + name); 21 return "hello " + name; 22 } 23 24 }
发布WebService,Endpoint(终端, 发布webservice),可以在服务器端编写拦截器,此拦截器可以替换掉tcp/ip监控工具。
1 package com.bie.webservice.endpoint; 2 3 import java.util.List; 4 5 import javax.xml.ws.Endpoint; 6 7 import org.apache.cxf.interceptor.Interceptor; 8 import org.apache.cxf.interceptor.LoggingInInterceptor; 9 import org.apache.cxf.interceptor.LoggingOutInterceptor; 10 import org.apache.cxf.jaxws22.EndpointImpl; 11 import org.apache.cxf.message.Message; 12 13 import com.bie.webservice.sei.HelloWebServiceSEI; 14 import com.bie.webservice.sei.impl.HelloWebServiceSEIImpl; 15 16 /** 17 * 18 * @author 1、发布WebService,Endpoint(终端, 发布webservice)。 19 * 20 */ 21 public class WebServiceEndpoint { 22 23 public static void main(String[] args) { 24 // 使用Endpoint发布webservice 25 // 参数一,url地址 26 String address = "http://localhost:8888/webservice/hello"; 27 // 参数二,是SEI实现类对象 28 HelloWebServiceSEI implementor = new HelloWebServiceSEIImpl(); 29 // 终端 30 Endpoint endpoint = Endpoint.publish(address, implementor); 31 System.out.println(endpoint.toString()); 32 33 // 将Endpoint转换为EndpointImpl,使用更多的实现方法 34 EndpointImpl endpointImpl = (EndpointImpl) endpoint; 35 // 如果要添加拦截器,是使用终端Endpoint来添加拦截器的,这里使用EndpointImpl来添加拦截器 36 // 添加服务器端的入拦截器 37 List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors(); 38 // 向拦截器集合中添加拦截器,添加服务器端的日志入拦截器 39 inInterceptors.add(new LoggingInInterceptor()); 40 41 // 添加服务器端的日志出拦截器 42 List<Interceptor<? extends Message>> outInterceptors = endpointImpl.getOutInterceptors(); 43 // 向拦截器集合中添加拦截器,添加服务器端的日志出拦截器 44 outInterceptors.add(new LoggingOutInterceptor()); 45 46 System.out.println("使用Endpoint发布webservice,发布成功Success......"); 47 } 48 49 }
可以使用eclipse的web service浏览器进行测试,查看入拦截器和出拦截器的请求和相应参数是什么,如下所示:
输入请求参数之后,就可以在控制台查看请求信息和响应信息,如下所示:
同样,在客户端也可以进行入拦截器和出拦截器的配置,客户端的代码可以使用java的工具脚本wsimport自动生成的,这里省略了,同样,需要将apache-cxf-2.5.9\lib里面的包导入到客户端项目中,如下所示:
1 package com.bie.webservice.sei.client; 2 3 import java.util.List; 4 5 import org.apache.cxf.endpoint.Client; 6 import org.apache.cxf.frontend.ClientProxy; 7 import org.apache.cxf.interceptor.Interceptor; 8 import org.apache.cxf.interceptor.LoggingInInterceptor; 9 import org.apache.cxf.interceptor.LoggingOutInterceptor; 10 import org.apache.cxf.message.Message; 11 12 import com.bie.webservice.sei.HelloWebServiceSEI; 13 import com.bie.webservice.sei.impl.HelloWebServiceSEIImplService; 14 15 public class WebServiceCxfClient { 16 17 public static void main(String[] args) { 18 // 根据标签进行创建类<wsdl:service name="HelloWebServiceSEIImplService"> 19 HelloWebServiceSEIImplService factory = new HelloWebServiceSEIImplService(); 20 // 根据绑定的type为返回类型,<wsdl:binding name="HelloWebServiceSEIImplServiceSoapBinding" 21 // type="ns1:HelloWebServiceSEI"> 22 HelloWebServiceSEI helloWebServiceSEI = factory.getHelloWebServiceSEIImplPort(); 23 24 // 服务器端使用的是终端EndPoint来进行添加拦截器的 25 // 客户端是是使用的ClientProxy来获取到Client,Client即发送请求的客户端对象 26 Client client = ClientProxy.getClient(helloWebServiceSEI); 27 // 客户端的日志出拦截器 28 List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors(); 29 // 向集合中添加出拦截器 30 outInterceptors.add(new LoggingOutInterceptor()); 31 32 // 客户端的日志入拦截器 33 List<Interceptor<? extends Message>> inInterceptors = client.getInInterceptors(); 34 // 向集合中添加入拦截器 35 inInterceptors.add(new LoggingInInterceptor()); 36 37 // 调用业务方法 38 String sayHello = helloWebServiceSEI.sayHello("张姗姗"); 39 System.out.println("client : " + sayHello); 40 } 41 42 }
客户端请求,入拦截器和出拦截器,已经请求方法返回信息打印的结果,如下所示:
4、CXF的拦截器API。
1)、Interceptor(拦截器接口)。
2)、AbstractPhaseInterceptor(自定义拦截器从此继承)。
3)、LoggingInInterceptor(系统日志入拦截器类)。使用日志拦截器,可以实现日志记录,日志拦截器有LoggingInInterceptor,LoggingOutInterceptor。
4)、LoggingOutInterceptor(系统日志出拦截器类)。使用日志拦截器,可以实现日志记录,日志拦截器有LoggingInInterceptor,LoggingOutInterceptor。
5、使用自定义拦截器,实现用户名与密码的检验,对于客户端的出拦截器,入拦截器,服务器端的入拦截器,出拦截器如何进行添加拦截器进行账号密码校验呢?
答:需要在服务器端的in拦截器,客户端的out拦截器添加拦截器进行校验。
首先搞一个客户端的出拦截器,进行拦截,如下所示:
1 package com.bie.webservice.sei.interceptor; 2 3 import java.util.List; 4 5 import javax.xml.namespace.QName; 6 import javax.xml.parsers.DocumentBuilder; 7 import javax.xml.parsers.DocumentBuilderFactory; 8 import javax.xml.parsers.ParserConfigurationException; 9 10 import org.apache.cxf.binding.soap.SoapMessage; 11 import org.apache.cxf.headers.Header; 12 import org.apache.cxf.interceptor.Fault; 13 import org.apache.cxf.phase.AbstractPhaseInterceptor; 14 import org.apache.cxf.phase.Phase; 15 import org.apache.xml.utils.DOMHelper; 16 import org.w3c.dom.Document; 17 import org.w3c.dom.Element; 18 19 /** 20 * 21 * @author 拦截的是某一个消息,所以泛型是SoapMessage 22 * 23 */ 24 public class ClientValidateUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 25 26 private String name;// 账号 27 private String password;// 密码 28 29 /** 30 * 此构造器是关键点,决定了什么时候拦截器会拦截到消息 31 * 32 * @param phase 33 */ 34 public ClientValidateUserInterceptor(String name, String password) { 35 // 准备协议化的时候进行拦截调用 36 super(Phase.PRE_PROTOCOL); 37 this.name = name; 38 this.password = password; 39 } 40 41 /** 42 * 请求体 43 * 44 * <soap:Envelope> 45 * <head> 46 * <zhangsansan> 47 * <name>zhangsansan</name> 48 * <password>123456</password> 49 * </zhangsansan> 50 * </head> 51 * <Body> 52 * <sayHello> 53 * <arg0>张姗姗</arg0> 54 * </sayHello> 55 * </Body> 56 * </soap:Envelope> 57 */ 58 @Override 59 public void handleMessage(SoapMessage soapMessage) throws Fault { 60 // 获取到头信息,向头部信息设置值 61 List<Header> headers = soapMessage.getHeaders(); 62 // 此时需要构造这种结构的数据 63 // <zhangsansan> 64 // <name>zhangsansan</name> 65 // <password>123456</password> 66 // </zhangsansan> 67 68 // 第一步:初始化一个XML解析工厂 69 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 70 // 第二步:创建一个DocumentBuilder实例 71 DocumentBuilder builder = null; 72 try { 73 builder = factory.newDocumentBuilder(); 74 } catch (ParserConfigurationException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 // 第三步:构建一个Document实例 79 Document doc = builder.newDocument(); 80 // standalone用来表示该文件是否呼叫其它外部的文件。若值是 ”yes” 表示没有呼叫外部文件 81 doc.setXmlStandalone(true); 82 // 第四步:创建一个根节点,名称为root,并设置一些基本属性,创建标签 83 Element rootElement = doc.createElement("zhangsansan"); 84 // 设置节点属性 85 // rootElement.setAttribute("attr", "root"); 86 // 设置标签之间的内容 87 // rootElement.setTextContent("root attr"); 88 89 // 开始设置<zhangsansan>下面的标签<name>zhangsansan</name> 90 Element nameElement = doc.createElement("name"); 91 nameElement.setTextContent(this.name); 92 // 第五步:把节点添加到Document中,再创建一些子节点加入,将子标签添加到父标签中 93 rootElement.appendChild(nameElement); 94 95 // 开始设置<zhangsansan>下面的标签<name>zhangsansan</name> 96 Element passwordElement = doc.createElement("password"); 97 passwordElement.setTextContent(this.password); 98 // 第五步:把节点添加到Document中,再创建一些子节点加入,将子标签添加到父标签中 99 rootElement.appendChild(passwordElement); 100 101 // 第六步:把构造的XML结构,写入到具体的文件中 102 // 参数一QName起一个唯一的名字,这个名称必须和rootElement标签的值必须一样 103 // 参数二就是rootElement根节点 104 Header header = new Header(new QName("zhangsansan"), rootElement); 105 // 将此请求体和构建的请求头发送给服务器端 106 headers.add(header); 107 108 System.out.println("Client handleMessage Interceptor......"); 109 110 // DOMHelper.createDocument()方法过期了 111 // Document createDocument = DOMHelper.createDocument(); 112 } 113 114 }
然后在客户端新增自定义的出拦截器,验证账号密码信息,如下所示:
1 package com.bie.webservice.sei.client; 2 3 import java.util.List; 4 5 import org.apache.cxf.endpoint.Client; 6 import org.apache.cxf.frontend.ClientProxy; 7 import org.apache.cxf.interceptor.Interceptor; 8 import org.apache.cxf.message.Message; 9 10 import com.bie.webservice.sei.HelloWebServiceSEI; 11 import com.bie.webservice.sei.impl.HelloWebServiceSEIImplService; 12 import com.bie.webservice.sei.interceptor.ClientValidateUserInterceptor; 13 14 public class WebServiceCxfClient2 { 15 16 public static void main(String[] args) { 17 // 根据标签进行创建类<wsdl:service name="HelloWebServiceSEIImplService"> 18 HelloWebServiceSEIImplService factory = new HelloWebServiceSEIImplService(); 19 // 根据绑定的type为返回类型,<wsdl:binding name="HelloWebServiceSEIImplServiceSoapBinding" 20 // type="ns1:HelloWebServiceSEI"> 21 HelloWebServiceSEI helloWebServiceSEI = factory.getHelloWebServiceSEIImplPort(); 22 23 // 服务器端使用的是终端EndPoint来进行添加拦截器的 24 // 客户端是是使用的ClientProxy来获取到Client,Client即发送请求的客户端对象 25 Client client = ClientProxy.getClient(helloWebServiceSEI); 26 // 账号密码的校验,要使用的是客户端的出拦截器 27 List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors(); 28 // 向集合中添加自定义的出拦截器 29 String name = "张姗姗"; 30 String password = "123456"; 31 outInterceptors.add(new ClientValidateUserInterceptor(name, password)); 32 33 // 调用业务方法 34 String sayHello = helloWebServiceSEI.sayHello("张姗姗"); 35 System.out.println("client : " + sayHello); 36 } 37 38 }
然后搞一个服务器端的入拦截器,验证账号密码是否正常,如下所示:
1 package com.bie.webservice.interceptor; 2 3 import javax.xml.namespace.QName; 4 5 import org.apache.cxf.binding.soap.SoapMessage; 6 import org.apache.cxf.headers.Header; 7 import org.apache.cxf.interceptor.Fault; 8 import org.apache.cxf.phase.AbstractPhaseInterceptor; 9 import org.apache.cxf.phase.Phase; 10 import org.w3c.dom.Element; 11 import org.w3c.dom.Node; 12 13 /** 14 * 15 * @author 服务器端的自定义入拦截器 16 * 17 */ 18 public class ServerValidateUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 19 20 private static final String name = "张姗姗"; 21 private static final String password = "123456"; 22 23 public ServerValidateUserInterceptor() { 24 // 准备协议化的时候进行拦截调用 25 super(Phase.PRE_PROTOCOL); 26 } 27 28 /** 29 * 请求体,服务器端需要解析请求头信息 30 * 31 * <soap:Envelope> 32 * <head> 33 * <zhangsansan> 34 * <name>zhangsansan</name> 35 * <password>123456</password> 36 * </zhangsansan> 37 * </head> 38 * <Body> 39 * <sayHello> 40 * <arg0>张姗姗</arg0> 41 * </sayHello> 42 * </Body> 43 * </soap:Envelope> 44 */ 45 @Override 46 public void handleMessage(SoapMessage soapMessage) throws Fault { 47 // 获取到请求头的信息 48 QName qName = QName.valueOf("zhangsansan"); 49 // 获取到请求头 50 Header header = soapMessage.getHeader(qName); 51 // 判断是否为空 52 if(header != null) { 53 // 获取到对象,强转为w3c的元素标签 54 Element element = (Element) header.getObject(); 55 // 获取到name标签的值 56 Node nameNode = element.getElementsByTagName("name").item(0); 57 // 获取到name的值 58 String nameValue = nameNode.getTextContent(); 59 // 获取到pasword标签的值 60 Node passwordNode = element.getElementsByTagName("password").item(0); 61 // 获取到pasword的值 62 String paswordValue = passwordNode.getTextContent(); 63 // 开始进行判断 64 if(ServerValidateUserInterceptor.name.equals(nameValue) && ServerValidateUserInterceptor.password.equals(paswordValue)) { 65 System.out.println("Server 通过拦截器......"); 66 return; 67 } 68 } 69 // 如果不能通过 70 System.out.println("Sorry Server 不通过拦截器......"); 71 // 抛出异常信息 72 throw new Fault(new RuntimeException("账号密码错误......")); 73 } 74 75 }
然后在服务器端添加自定义的入拦截器,如下所示:
1 package com.bie.webservice.endpoint; 2 3 import java.util.List; 4 5 import javax.xml.ws.Endpoint; 6 7 import org.apache.cxf.interceptor.Interceptor; 8 import org.apache.cxf.jaxws22.EndpointImpl; 9 import org.apache.cxf.message.Message; 10 11 import com.bie.webservice.interceptor.ServerValidateUserInterceptor; 12 import com.bie.webservice.sei.HelloWebServiceSEI; 13 import com.bie.webservice.sei.impl.HelloWebServiceSEIImpl; 14 15 /** 16 * 17 * @author 1、发布WebService,Endpoint(终端, 发布webservice)。 18 * 19 */ 20 public class WebServiceEndpoint2 { 21 22 public static void main(String[] args) { 23 // 使用Endpoint发布webservice 24 // 参数一,url地址 25 String address = "http://localhost:8888/webservice/hello"; 26 // 参数二,是SEI实现类对象 27 HelloWebServiceSEI implementor = new HelloWebServiceSEIImpl(); 28 // 终端 29 Endpoint endpoint = Endpoint.publish(address, implementor); 30 System.out.println(endpoint.toString()); 31 32 // 将Endpoint转换为EndpointImpl,使用更多的实现方法 33 EndpointImpl endpointImpl = (EndpointImpl) endpoint; 34 // 如果要添加拦截器,是使用终端Endpoint来添加拦截器的,这里使用EndpointImpl来添加拦截器 35 // 添加服务器端的入拦截器 36 List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors(); 37 // 向拦截器集合中添加拦截器,添加服务器端的账号密码验证的入拦截器 38 inInterceptors.add(new ServerValidateUserInterceptor()); 39 40 System.out.println("使用Endpoint发布webservice,发布成功Success......"); 41 } 42 43 }
启动服务器端服务,然后启动客户端服务,分别输入正确的账号密码,和错误的账号密码进行验证。