1、什么是拦截器?
拦截器分为Service端和Client端。 拦截器是在发送soap消息包的某一个时机拦截soap消息包,对soap消息包的数据进行分析或处理。分为CXF自带的拦截器和自定义的拦截器
CXF自带的拦截器:
LoggingInInterceptor(系统日志入拦截器类)
LoggingOutInterceptor(系统日志出拦截器类)
下面一张图可以形象的表现出拦截器的实现原理。
1.1拦截器链的阶段:
拦截器链有多个阶段,每个阶段都有多个拦截器。拦截器在拦截器链的哪个阶段起作用,可以在拦截器的构造函数中声明。
输入拦截器链有如下几个阶段,这些阶段按照在拦截器链中的先后顺序排列。
阶段名称 | 阶段功能描述 |
---|---|
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已经实现了多种拦截器,如操纵消息头、执行认证检查、验证消息数据、日志记录、消息压缩等,有些拦截器在发布服务、访问服务时已经默认添加到拦截器链。
注意: jdk写的程序不支持拦截器,需要使用cxf框架,导入cxf的jar包
1.2拦截器能干吗?
我们知道webservice是实现两个系统之间的交互,服务器在接受客户端传来的请求时,必然需要去验证其是否具有权限操作。
通常呢,我们是把验证信息放在soapmessage的头信息中。比如说实现用户的登录验证等等。或者更加深入的是拦截器其实内部之间是可以相互通讯的。
2、查看CXF自带的拦截器
2.1 服务器端实现
package com.ws.service;
import javax.xml.ws.Endpoint;
import com.sun.xml.internal.ws.transport.http.server.EndpointImpl;
import com.ws.HelloWSImpl;
public class ServerTest {
public static void main(String[] args) {
String address="http://localhost:8080/WS_day_01/HelloWS/sayHello";
//调用Endpoint的publish方法
EndpointImpl ep=(EndpointImpl)Endpoint.publish(address, new HelloWSImpl());
/*ep.getInInterceptors().add(new LoggingInInterceptor());
ep.getInInterceptors().add(new LoggingInInterceptor());*/
System.out.println("发布成功");
}
}
2.2 客户端实现
package com.ws.service;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import com.ws.HelloWSImpl;
public class ServerTest {
public static void main(String[] args) {
String address="http://localhost:8080/WS_day_01/HelloWS/sayHello";
//调用Endpoint的publish方法
EndpointImpl ep=(EndpointImpl)Endpoint.publish(address, new HelloWSImpl());
ep.getInInterceptors().add(new LoggingInInterceptor());
ep.getInInterceptors().add(new LoggingInInterceptor());
System.out.println("发布成功");
}
}
2.3输出结果是:
客户端:
服务器端:
3、自定义拦截器
3.1 服务器端
package com.ws.intecepter;
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;
/**
* 自定义拦截器,需要继承 AbstractPhaseInterceptor
* 通过PhaseInterceptor,可以指定拦截器在哪个阶段起作用
* @author SAM
*
*/
public class MyIntecepter extends AbstractPhaseInterceptor<SoapMessage> {
/*
* 一定要自己定义一个构造器
*/
public MyIntecepter() {
/*
* 父类构造器中参数String phase,为在哪个阶段调用拦截器
* Phase.PRE_INVOKE在调用之前调用拦截器
*/
super(Phase.PRE_INVOKE);
}
/* 实现自己的拦截器时,需要实现handleMessage方法
* handleMessage方法中的形参就是被拦截到的SOAP消息
* 一旦程序获得了这个soap消息,剩下的事情就可以解析soap消息,或修改soap消息
*/
public void handleMessage(SoapMessage msg) throws Fault {
System.out.println("++++拦++++截++++器++++开++++始++++调++++用++++");
// System.out.println("=========================== \n"+ msg+"\n ========================");
List<Header> headers = msg.getHeaders();
if(headers==null || headers.size()<1) {
throw new Fault(new IllegalArgumentException("根本没有Header,别想调用"));
}
//假如要求第一个Header携带了用户名、密码信息
Header firstHeader = headers.get(0);
Element element = (Element) firstHeader.getObject();
NodeList userids = element.getElementsByTagName("userId");
NodeList userPasses = element.getElementsByTagName("userPass");
if(userids.getLength()!=1) {
throw new Fault(new IllegalArgumentException("用户名格式不对"));
}
if(userPasses.getLength()!=1) {
throw new Fault(new IllegalArgumentException("密码格式不对"));
}
//得到第一个userId元素的文本内容,以该内容作为用户名字
String userId = userids.item(0).getTextContent();
String userPass = userPasses.item(0).getTextContent();
//实际项目中,应该去查询数据库,该用户名、密码是否被授权
if(userId.equals("冯冬冬")) {
//throw new Fault(new IllegalArgumentException("---------用---户----名---不----正-----确---------!!!!"));
System.out.println("拦截器内部用户名正确");
}
if(userPass.equals("123456")) {
//throw new Fault(new IllegalArgumentException("--------密---码-----不-----正------确-------!!!!!"));
System.out.println("拦截器内部密码正确");
}
System.out.println("拦++++截++++器++++结===============束++++");
}
}
接下来添加自己的拦截器”
package com.ws.publish;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import com.ws.impl.HelloWorldImpl;
import com.ws.intecepter.MyIntecepter;
/**
* 类描述:
*@author: 冯冬冬
*@date: 日期:2018-7-25 时间:上午9:41:41
*/
public class PublishTest {
public static void main(String[] args) {
System.out.println("Server start ……");
HelloWorldImpl service = new HelloWorldImpl();
String address = "http://127.0.0.1:8888/helloWorld";
// 调用Endpoint的publish方法
EndpointImpl ep=(EndpointImpl)Endpoint.publish(address, service);
//在服务器端添加自带拦截器
//ep.getInInterceptors().add(new LoggingInInterceptor());
//ep.getInInterceptors().add(new LoggingInInterceptor());
//添加自定义拦截器
ep.getInInterceptors().add(new MyIntecepter());
System.out.println("server ready ……");
}
}
3.2 客户端
/**
*
*/
package com.ws.intecepter;
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;
/**
* 类描述:
*@author: 冯冬冬
*@date: 日期:2018-7-25 时间:下午2:41:38
*/
public class MyClientIntecepter extends AbstractPhaseInterceptor<SoapMessage> {
private String userId;
private String userPass;
//构造器
public MyClientIntecepter(String userId, String userPass) {
super(Phase.PREPARE_SEND);//发送消息前调用
this.userId = userId;
this.userPass = userPass;
}
// 实现的方法
public void handleMessage(SoapMessage msg) throws Fault {
System.out.println("=================客 户 端 内部拦截器调用 开 始================");
List<Header> headers = msg.getHeaders();//得到soap消息的header
//创建document对象
Document document = DOMUtils.createDocument();
Element header = document.createElement("MyHeader");
//创建一个header元素与服务器一致
Element userIdEle = document.createElement("userId");
Element userPassEle = document.createElement("userPass");
userIdEle.setTextContent(userId);
userPassEle.setTextContent(userPass);
header.appendChild(userIdEle);
header.appendChild(userPassEle);
System.out.println("=================客 户 端 内部拦截器调用 结 束================");
headers.add(new Header(new QName("冯冬冬"), header));//把header放入soap的
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPass() {
return userPass;
}
public void setUserPass(String userPass) {
this.userPass = userPass;
}
}
接下来在客户端添加自定义拦截器:
package com.ws.test;
import java.util.List;
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.ws.Cat;
import com.ws.HelloWorld;
import com.ws.User;
import com.ws.impl.HelloWorldImplService;
import com.ws.intecepter.MyClientIntecepter;
/**
* 类描述:
*@author: 冯冬冬
*@date: 日期:2018-7-25 时间:上午10:33:30
*/
public class TestClient {
public static void main(String[] args) {
HelloWorldImplService factory=new HelloWorldImplService();
HelloWorld hw=factory.getHelloWorldImplPort();
Client client = ClientProxy.getClient(hw);
/*// 客户端 增加拦截器。这里是使用的自带的拦截器
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());*/
//下面使用自定义的拦截器
client.getOutInterceptors().add(new MyClientIntecepter("冯冬冬","123456"));
System.out.print(""+hw.sayHi("张三"));
}
}
3.3 输出结果
首先看客户端:
接下来看服务器端:
总结:拦截器的主要作用就是实现权限拦截。对用户的非法操作进行拦截。基本上就介绍到这里