【WebService】WebService之CXF的拦截器(五)

时间:2022-12-26 10:21:28

CXF拦截器介绍

  CXF拦截器是功能的主要实现单元,也是主要的扩展点,可以在不对核心模块进行修改的情况下,动态添加功能。当服务被调用时,会经过多个拦截器链(Interceptor Chain)处理,拦截器链在服务输入(IN)或输出(OUT)阶段实现附加功能,拦截器可以在客户端加入,也可以在服务端加入。

  工作示意图如下:
    【WebService】WebService之CXF的拦截器(五)

  

  拦截器的拦截阶段:
    拦截器有多个阶段,每个阶段都有多个拦截器。拦截器在哪个阶段起作用,可以在拦截器的构造函数中声明。 

输入拦截器有如下几个阶段,这些阶段按照在拦截器中的先后顺序排列。
阶段名称 阶段功能描述
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包:
      【WebService】WebService之CXF的拦截器(五)
    调用代码如下:

 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服务端,然后使用客户端进行调用。控制台输出如下:
    服务端控制台输出:
      【WebService】WebService之CXF的拦截器(五)
    客户端控制台输出:
      【WebService】WebService之CXF的拦截器(五)

自定义拦截器

  下面使用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、服务端控制台输出如下,可以看到自定义拦截器已经启用,并且获取到了用户名和密码进行验证。
      【WebService】WebService之CXF的拦截器(五)

  4、客户端控制台输出如下,可以看到自定义拦截器已经启用,并且添加了用户名和密码到消息的头里面。
      【WebService】WebService之CXF的拦截器(五)

  注:通过控制台可以看出,webservice服务器收到的是一段xml,而返回的也是xml,所有在调用webservcie的时候,可以使用ajax的post请求或者java里面HttpURLConnection来发送请求xml,然后获取响应xml进行处理。