CXF拦截器介绍
CXF拦截器是功能的主要实现单元,也是主要的扩展点,可以在不对核心模块进行修改的情况下,动态添加功能。当服务被调用时,会经过多个拦截器链(Interceptor Chain)处理,拦截器链在服务输入(IN)或输出(OUT)阶段实现附加功能,拦截器可以在客户端加入,也可以在服务端加入。
工作示意图如下:
拦截器的拦截阶段:
拦截器有多个阶段,每个阶段都有多个拦截器。拦截器在哪个阶段起作用,可以在拦截器的构造函数中声明。
阶段名称 | 阶段功能描述 |
---|---|
RECEIVE | Transport level processing(接收阶段,传输层处理) |
(PRE/USER/POST)_STREAM | Stream level processing/transformations(流处理/转换阶段) |
READ | This is where header reading typically occurs(SOAPHeader读取) |
(PRE/USER/POST)_PROTOCOL | Protocol processing, such as JAX-WS SOAP handlers(协议处理阶段,例如JAX-WS的Handler处理) |
UNMARSHAL | Unmarshalling of the request(SOAP请求解码阶段) |
(PRE/USER/POST)_LOGICAL | Processing of the umarshalled request(SOAP请求解码处理阶段) |
PRE_INVOKE | Pre invocation actions(调用业务处理之前进入该阶段) |
INVOKE | Invocation of the service(调用业务阶段) |
POST_INVOKE | Invocation of the outgoing chain if there is one(提交业务处理结果,并触发输入连接器) |
阶段名称 | 阶段功能描述 |
---|---|
SETUP | Any set up for the following phases(设置阶段) |
(PRE/USER/POST)_LOGICAL | Processing of objects about to marshalled |
PREPARE_SEND | Opening of the connection(消息发送准备阶段,在该阶段创建Connection) |
PRE_STREAM | 流准备阶段 |
PRE_PROTOCOL | Misc protocol actions(协议准备阶段) |
WRITE | Writing of the protocol message, such as the SOAP Envelope.(写消息阶段) |
MARSHAL | Marshalling of the objects |
(USER/POST)_PROTOCOL | Processing of the protocol message |
(USER/POST)_STREAM | Processing of the byte level message(字节处理阶段,在该阶段把消息转为字节) |
SEND | 消息发送 |
在CXF中,所有对消息的处理都是通过各种拦截器实现。CXF已经实现了多种拦截器,如操纵消息头、执行认证检查、验证消息数据、日志记录、消息压缩等,有些拦截器在发布服务、访问服务时已经默认添加到拦截器。
日志拦截器
首先使用CXF搭建好WebService的客户端以及服务端。参照(【WebService】使用CXF开发WebService(四)),下例中使用的是上一篇的工程。
1、服务器端的日志拦截器:主要是在发布的时候,添加输入和输出日志拦截器。代码如下:
1 package com.test.ws.server; 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.test.ws.HelloWSImpl; 14 15 /** 16 * 发布Web Service 17 * @author H__D 18 * @date 2017年7月28日 上午11:40:48 19 * 20 */ 21 public class ServerTest { 22 23 public static void main(String[] args) { 24 25 //定义WebService的发布地址,这个地址就是提供给外界访问Webervice的URL地址,URL地址格式为:http://ip:端口号/xxxx 26 String address = "http://127.0.0.1:8989/test-webservice/hellows"; 27 //使用Endpoint类提供的publish方法发布WebService,发布时要保证使用的端口号没有被其他应用程序占用 28 Endpoint endpoint = Endpoint.publish(address, new HelloWSImpl()); 29 30 //打印endpoint,可以看到endpoint实际上是一个 org.apache.cxf.jaxws22.EndpointImpl 对象 31 System.out.println("endpoint -->" + endpoint); 32 33 //强转为EndpointImpl对象 34 EndpointImpl endpointImpl = (EndpointImpl) endpoint; 35 36 //服务端的日志入拦截器 37 List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors(); 38 inInterceptors.add(new LoggingInInterceptor()); 39 40 //服务器端的日志出拦截器 41 List<Interceptor<? extends Message>> outInterceptors = endpointImpl.getOutInterceptors(); 42 outInterceptors.add(new LoggingOutInterceptor()); 43 44 System.out.println("发布webservice成功!"); 45 46 } 47 }
2、客服端的日志拦截器:主要是在调整WebService的时候,添加输入和输出日志拦截器。(注:由于使用的是CXF拦截器,所以客户端也需要添加CXF相应的jar包)
客户端添加CXF的jar包:
调用代码如下:
1 package com.test.ws.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.test.ws.HelloWS; 13 import com.test.ws.HelloWSImplService; 14 15 /** 16 * 调用WebService的客户端 17 * @author H__D 18 * @date 2017年7月28日 下午2:39:24 19 * 20 */ 21 public class WSClient { 22 23 public static void main(String[] args) { 24 //创建一个用于产生WebServiceImpl实例的工厂,WebServiceImplService类是wsimport工具生成的 25 HelloWSImplService factory = new HelloWSImplService(); 26 //通过工厂生成一个WebServiceImpl实例,WebServiceImpl是wsimport工具生成的 27 HelloWS helloWS = factory.getHelloWSImplPort(); 28 System.out.println(helloWS.getClass()); 29 30 //发送请求的客户端对象 31 Client client = ClientProxy.getClient(helloWS); 32 33 //客户端的日志入拦截器 34 List<Interceptor<? extends Message>> inInterceptors = client.getInInterceptors(); 35 inInterceptors.add(new LoggingInInterceptor()); 36 37 //客户端的日志出拦截器 38 List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors(); 39 outInterceptors.add(new LoggingOutInterceptor()); 40 41 //调用WebService的sayHello方法 42 String result = helloWS.sayHello("Jack"); 43 System.out.println(result); 44 } 45 46 }
3、发布WebService服务端,然后使用客户端进行调用。控制台输出如下:
服务端控制台输出:
客户端控制台输出:
自定义拦截器
下面使用cxf自定义拦截器,客户端使用自定义拦截器添加用户信息,服务端使用自定义 拦截器获取用户信息并验证
1、在服务端工程编写自定义拦截器,验证用户信息,代码如下
1 package com.test.wx.interceptor; 2 3 import java.util.List; 4 5 import javax.xml.namespace.QName; 6 7 import org.apache.cxf.binding.soap.SoapMessage; 8 import org.apache.cxf.headers.Header; 9 import org.apache.cxf.interceptor.Fault; 10 import org.apache.cxf.phase.AbstractPhaseInterceptor; 11 import org.apache.cxf.phase.Phase; 12 import org.w3c.dom.Element; 13 14 /** 15 * 服务端权限拦截器 16 * @author H__D 17 * @date 2017年8月2日 下午2:22:02 18 * 19 */ 20 public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 21 22 public AuthInterceptor() { 23 super(Phase.PRE_INVOKE); //拦截器在调用方法之前拦截SOAP消息 24 } 25 26 /** 27 * 拦截器操作 28 * 信息如下 29 * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 30 * <soap:Header> 31 * <authHeader> 32 * <name>hd</name> 33 * <password>123456</password> 34 * </authHeader> 35 * </soap:Header> 36 * <soap:Body> 37 * <ns2:sayHello xmlns:ns2="http://ws.test.com/"> 38 * <arg0>Jack</arg0> 39 * </ns2:sayHello> 40 * </soap:Body> 41 * </soap:Envelope> 42 */ 43 @Override 44 public void handleMessage(SoapMessage msg) throws Fault { 45 System.out.println("com to auth interceptor..."); 46 47 //获取SOAP信息的所有Header 48 List<Header> headers = msg.getHeaders(); 49 50 if(headers == null || headers.size() < 1) 51 { 52 throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截")); 53 } 54 55 boolean isAuth = false; 56 //获取Header携带的用户名和密码信息 57 for (Header header : headers) { 58 //判断认证信息头 59 if(new QName("authHeader").equals(header.getName())) 60 { 61 //提取认证信息 62 Element element = (Element) header.getObject(); 63 String name = element.getElementsByTagName("name").item(0).getTextContent(); 64 String password = element.getElementsByTagName("password").item(0).getTextContent(); 65 66 if(name.equals("hd") && password.equals("123456")) 67 { 68 isAuth = true; 69 break; 70 } 71 } 72 } 73 74 if(isAuth) 75 { 76 System.out.println("认证成功!!!"); 77 }else 78 { 79 throw new Fault(new IllegalArgumentException("用户名或密码不正确")); 80 } 81 } 82 83 }
服务端设置好自定义拦截器,并进行发布,代码如下:
1 package com.test.ws.server; 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.test.ws.HelloWSImpl; 14 import com.test.wx.interceptor.AuthInterceptor; 15 16 /** 17 * 发布Web Service 18 * @author H__D 19 * @date 2017年7月28日 上午11:40:48 20 * 21 */ 22 public class ServerTest { 23 24 public static void main(String[] args) { 25 26 //定义WebService的发布地址,这个地址就是提供给外界访问Webervice的URL地址,URL地址格式为:http://ip:端口号/xxxx 27 String address = "http://127.0.0.1:8989/test-webservice/hellows"; 28 //使用Endpoint类提供的publish方法发布WebService,发布时要保证使用的端口号没有被其他应用程序占用 29 Endpoint endpoint = Endpoint.publish(address, new HelloWSImpl()); 30 31 //打印endpoint,可以看到endpoint实际上是一个 org.apache.cxf.jaxws22.EndpointImpl 对象 32 System.out.println("endpoint -->" + endpoint); 33 34 //强转为EndpointImpl对象 35 EndpointImpl endpointImpl = (EndpointImpl) endpoint; 36 37 //服务端的日志入拦截器 38 List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors(); 39 inInterceptors.add(new LoggingInInterceptor()); 40 41 //服务端的自定义拦截器:验证用户名和密码 42 inInterceptors.add(new AuthInterceptor()); 43 44 //服务器端的日志出拦截器 45 List<Interceptor<? extends Message>> outInterceptors = endpointImpl.getOutInterceptors(); 46 outInterceptors.add(new LoggingOutInterceptor()); 47 48 System.out.println("发布webservice成功!"); 49 50 } 51 }
2、在客户端编写自定义拦截器,添加用户信息,代码如下:
1 package com.test.interceptor; 2 3 import java.util.List; 4 5 import javax.xml.namespace.QName; 6 7 import org.apache.cxf.binding.soap.SoapMessage; 8 import org.apache.cxf.headers.Header; 9 import org.apache.cxf.helpers.DOMUtils; 10 import org.apache.cxf.interceptor.Fault; 11 import org.apache.cxf.phase.AbstractPhaseInterceptor; 12 import org.apache.cxf.phase.Phase; 13 import org.w3c.dom.Document; 14 import org.w3c.dom.Element; 15 16 /** 17 * 客户端添加用户信息拦截器 18 * @author H__D 19 * @date 2017年8月2日 下午2:47:08 20 * 21 */ 22 public class AddUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 23 24 private String name; 25 private String password; 26 27 public AddUserInterceptor(String name, String password) { 28 29 super(Phase.PRE_PROTOCOL);//准备协议化时拦截 30 this.name = name; 31 this.password = password; 32 } 33 34 /** 35 * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 36 * <soap:Header> 37 * <authHeader> 38 * <name>hd</name> 39 * <password>123456</password> 40 * </authHeader> 41 * </soap:Header> 42 * <soap:Body> 43 * <ns2:sayHello xmlns:ns2="http://ws.test.com/"> 44 * <arg0>Jack</arg0> 45 * </ns2:sayHello> 46 * </soap:Body> 47 * </soap:Envelope> 48 */ 49 @Override 50 public void handleMessage(SoapMessage msg) throws Fault { 51 //获取消息头 52 List<Header> headers = msg.getHeaders(); 53 54 //创建文档 55 Document document = DOMUtils.createDocument(); 56 //创建根目录 57 Element rootEle = document.createElement("authHeader"); 58 59 //配置head信息的用户名和密码 60 Element nameEle = document.createElement("name"); 61 nameEle.setTextContent(name); 62 Element passwordEle = document.createElement("password"); 63 passwordEle.setTextContent(password); 64 65 rootEle.appendChild(nameEle); 66 rootEle.appendChild(passwordEle); 67 //将信息添加到头 68 headers.add(new Header(new QName("authHeader"), rootEle)); 69 } 70 71 }
客户端添加自定义拦截器,并调用WebService服务,代码如下:
1 package com.test.ws.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.test.interceptor.AddUserInterceptor; 13 import com.test.ws.HelloWS; 14 import com.test.ws.HelloWSImplService; 15 16 /** 17 * 调用WebService的客户端 18 * @author H__D 19 * @date 2017年7月28日 下午2:39:24 20 * 21 */ 22 public class WSClient { 23 24 public static void main(String[] args) { 25 //创建一个用于产生WebServiceImpl实例的工厂,WebServiceImplService类是wsimport工具生成的 26 HelloWSImplService factory = new HelloWSImplService(); 27 //通过工厂生成一个WebServiceImpl实例,WebServiceImpl是wsimport工具生成的 28 HelloWS helloWS = factory.getHelloWSImplPort(); 29 System.out.println(helloWS.getClass()); 30 31 //发送请求的客户端对象 32 Client client = ClientProxy.getClient(helloWS); 33 34 //客户端的日志入拦截器 35 List<Interceptor<? extends Message>> inInterceptors = client.getInInterceptors(); 36 inInterceptors.add(new LoggingInInterceptor()); 37 38 //客户端的日志出拦截器 39 List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors(); 40 outInterceptors.add(new LoggingOutInterceptor()); 41 42 //添加自定义输出拦截器 43 outInterceptors.add(new AddUserInterceptor("hd", "123456")); 44 45 //调用WebService的sayHello方法 46 String result = helloWS.sayHello("Jack"); 47 System.out.println(result); 48 } 49 50 }
3、服务端控制台输出如下,可以看到自定义拦截器已经启用,并且获取到了用户名和密码进行验证。
4、客户端控制台输出如下,可以看到自定义拦截器已经启用,并且添加了用户名和密码到消息的头里面。
注:通过控制台可以看出,webservice服务器收到的是一段xml,而返回的也是xml,所有在调用webservcie的时候,可以使用ajax的post请求或者java里面HttpURLConnection来发送请求xml,然后获取响应xml进行处理。