一、用途
CXF拦截器类似Struts2的拦截器,后者是拦截和处理请求,前者是对发送和接收的sope消息进行处理,一般用于WS请求响应中的权限验证、日志记录,Soap消息处理,消息的压缩处理等;
这个拦截器可以直接访问和修改sope消息。
拿权限验证举例:
二、服务端添加拦截器
三种方式:JaxWsServerFactoryBean、Endpoint都可以通过getInInterceptors方法,向WebService服务添加拦截器,还可以自定义拦截器
1、Endpoint方式
package ws; import javax.xml.ws.Endpoint; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.EndpointImpl; import ws.impl.HelloWordImpl; public class ServerMain { public static void main(String[] args) { HelloWordI hw = new HelloWordImpl(); EndpointImpl ep = (EndpointImpl)Endpoint.publish("http://192.168.0.105/test", hw); //添加In拦截器 ep.getInInterceptors().add(new LoggingInInterceptor()); //添加Out拦截器 ep.getOutInterceptors().add(new LoggingOutInterceptor()); System.out.println("WebService 暴露成功!"); } }
2、JaxWsServerFactoryBean方式
package ws; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import ws.impl.HelloWordImpl; public class ServerMain { public static void main(String[] args) { HelloWordImpl hw = new HelloWordImpl(); //EndpointImpl ep = (EndpointImpl)Endpoint.publish("http://192.168.0.105/test", hw); //添加In拦截器 //ep.getInInterceptors().add(new LoggingInInterceptor()); //添加Out拦截器 //ep.getOutInterceptors().add(new LoggingOutInterceptor()); JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean(); factory.setAddress("http://192.168.0.105/test"); factory.setServiceClass(HelloWordI.class); factory.setServiceBean(hw); factory.getInInterceptors().add(new LoggingInInterceptor()); factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.create(); System.out.println("WebService 暴露成功!"); } }
以上两种方式 实现了接口InterceptorProvider:拦截器链InterceptorChain由许多Interceptor组成,Cxf中定义了一个接口InterceptorProvider,通过该接口可以获取到与当前对象绑定的拦截器链里面的所有拦截器,当我们需要往某对象现有的拦截器链里面添加拦截器的时候我们就可以往通过InterceptorProvider获取到的对应拦截器列表中添加相应的拦截器来实现。InterceptorProvider的定义如下。
public interface InterceptorProvider {
List<Interceptor<?extends Message>>getInInterceptors();
List<Interceptor<?extends Message>>getOutInterceptors();
List<Interceptor<?extends Message>>getInFaultInterceptors();
List<Interceptor<?extends Message>>getOutFaultInterceptors();
}
3、创建自定义拦截器
CXF已经实现了很多种拦截器,很多已经在发布、访问Web 服务时已经默认添加到拦截器链。一般情况下, 我们自己的拦截器只要继承AbstractPhaseInterceptor<T extends org.apache.cxf.message.Message>类即可(也可以实现PhaseInterceptor<T>接口),这个类可以指定继承它的拦截器在什么阶段被启用,阶段属性可以通过org.apache.cxf.phase.Phase 中的常量指定值。
package ws.interceptor; 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; /** * 类说明 * @author wangjunyu * @createDate 2016-10-20 下午8:34:24 * @version V1.0 */ public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
//构造方法指定拦截器在什么地方生效
//Phase中常量指定生效位置 如PRE_INVOKE表示调用之前拦截
//RECEIVE 接收消息阶段有效 即使配置在OutInterceptor 的集合中也无效
public AuthInterceptor() { super(Phase.PRE_INVOKE); } /** * 自定义拦截器需要实现handleMessage方法,该方法抛出Fault异常,可以自定义异常继承自Fault, * 也可以new Fault(new Throwable()) */ public void handleMessage(SoapMessage sope) throws Fault { System.out.println("开始验证用户信息!"); List<Header> headers = sope.getHeaders(); if (headers == null || headers.size() < 1) { throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息")); } Header header = headers.get(0); Element el = (Element)header.getObject(); NodeList users = el.getElementsByTagName("username"); NodeList passwords = el.getElementsByTagName("password"); if (users.getLength() < 1) { throw new IllegalArgumentException("找不到用户信息"); } String username = users.item(0).getTextContent().trim(); if (passwords.getLength() < 1) { throw new IllegalArgumentException("找不到用户密码"); } String password = passwords.item(0).getTextContent().trim(); //检查用户名和密码是否正确 if(!"admin".equals(username) || !"admin".equals(password)) { throw new Fault(new IllegalArgumentException("用户名或密码不正确")); } else { System.out.println("用户名密码正确允许访问"); } } }
三、客户端添加拦截器
package ws.interceptor; 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 wangjunyu * @createDate 2016-10-20 下午8:53:16 * @version V1.0 */ public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> { public ClientLoginInterceptor(String username, String password) { super(Phase.PREPARE_SEND); this.username = username; this.password = password; } private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public void handleMessage(SoapMessage soap) throws Fault { List<Header> headers = soap.getHeaders(); Document doc = DOMUtils.createDocument(); Element auth = doc.createElement("authrity"); Element username = doc.createElement("username"); Element password = doc.createElement("password"); username.setTextContent(this.username); password.setTextContent(this.password); auth.appendChild(username); auth.appendChild(password); headers.add(0, new Header(new QName("tiamaes"),auth)); } }
package ws; import org.apache.cxf.endpoint.Client; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; import ws.interceptor.ClientLoginInterceptor; /** * 类说明 * @author wangjunyu * @createDate 2016-7-10 上午11:24:09 * @version V1.0 */ public class ClientMain { /** * 获取客户端的两种方式*/ public static void main(String[] args) { /* HelloWordImpl hwproxy = new HelloWordImpl(); HelloWordI hw= hwproxy.getHelloWordImplPort();
Client client = ClientProxy.getClient(hw);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
User a = new User(); a.setName("哈哈"); List<User> t = hw.getUsers(a); //System.out.println(t.get(0).getName()); StringUser u = hw.getSecUsers(); System.out.println(u.getValues().get(0).getValue().getName()); System.out.println(u.getValues().get(1).getValue().getName()); */ JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); Client client = dcf.createClient("http://192.168.0.104/test?wsdl"); client.getInInterceptors().add(new LoggingInInterceptor()); client.getOutInterceptors().add(new ClientLoginInterceptor("admin","admin")); try { Object[] objs = client.invoke("syaHello", "Tom"); System.out.println(objs[0].toString()); } catch (Exception e) { e.printStackTrace(); } } }
四、SpringMVC中配置拦截器
1、cxf配置文件方式
1.1 单个拦截器配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <!-- 单个拦截器 --> <bean id="inMessageInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" /> <bean id="outMessageInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/> <bean id="authorInterceptor" class="com.buss.app.interceptor.AuthInterceptor"/> <!-- 登录验证服务 --> <jaxws:endpoint id="LoginServiceI" implementor="com.buss.app.login.LoginServiceImpl" address="/LoginService"> <!-- 输入日志拦截器 --> <jaxws:inInterceptors> <ref bean="inMessageInterceptor"/> <ref bean="authorInterceptor"/> </jaxws:inInterceptors> <!-- 输出日志拦截器 --> <jaxws:outInterceptors> <ref bean="outMessageInterceptor"/> </jaxws:outInterceptors> </jaxws:endpoint> <!-- APP首页服务 --> <jaxws:endpoint id="HomeServiceI" implementor="com.buss.app.home.HomeServiceImpl" address="/HomeService" /> </beans>
1.2 捆绑拦截器打包配置
由于不光CXF内置有拦截器,而且还可以自定义拦截器。这样WebServcie的SEI可能配置多个、一大堆拦截器,这样很不方便。在Struts2中可以自定义拦截器,他还提供了自定义拦截器堆栈的功能,将多个拦截器捆绑在一起使用。这样不必要一个一个的去注册拦截器。在CXF中也有类似功能,可以将拦截器捆绑在一起,你就可以将它注册到你要使用的地方,而不必一个一个拦截器的注册使用。 实现拦截器的捆绑过程非常的简单,继承AbstractFeature 类来实现一个新的特征, 只需要覆盖initializeProvider 方法即可。其实Feature 就是将一组拦截器放在其中,然后一并注册使用。
具体如下:
首先定义一个捆绑拦截器类
package com.buss.app.interceptor; import org.apache.cxf.Bus; import org.apache.cxf.feature.AbstractFeature; import org.apache.cxf.interceptor.InterceptorProvider; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; public class PackageInterceptorFeature extends AbstractFeature { protected void initializeProvider(InterceptorProvider provider, Bus bus) { provider.getInInterceptors().add(new LoggingInInterceptor()); provider.getInInterceptors().add(new AuthInterceptor()); provider.getOutInterceptors().add(new LoggingOutInterceptor()); } }
然后再在配置文件使用<jaxws:features>一起配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <!-- 登录验证服务 --> <jaxws:endpoint id="LoginServiceI" implementor="com.buss.app.login.LoginServiceImpl" address="/LoginService"> <!-- 捆绑拦截器 --> <jaxws:features> <bean class="com.buss.app.interceptor.PackageInterceptorFeature"></bean> </jaxws:features> </jaxws:endpoint> <!-- APP首页服务 --> <jaxws:endpoint id="HomeServiceI" implementor="com.buss.app.home.HomeServiceImpl" address="/HomeService" /> </beans>
2、注解配置拦截器
Cxf为四种类型的拦截器都提供了对应的注解,以方便用户直接在SEI上进行配置,对应的注解如下。
- org.apache.cxf.interceptor.InInterceptors
- org.apache.cxf.interceptor.InFaultInterceptors
- org.apache.cxf.interceptor.OutInterceptors
- org.apache.cxf.interceptor.OutFaultInterceptors
每个注解都对应有两个属性,String[]类型的interceptors和Class<? extends Interceptor<? extendsMessage>>[]类型的classes,其中interceptors用来指定需要配置的拦截器的全名称,而classes则用来指定需要配置的拦截器的类。以下是一个在SEI上通过@InInterceptor配置了入拦截器LogInterceptor的示例。
@InInterceptors(classes={LogInterceptor.class})
@WebService
public interface HelloWorld {
public String sayHi(String who);
}
如果在配置的时候既使用了classes属性配置,又使用了interceptors属性配置,那么两个配置都会生效。如下代码就相当于我们配置了两个自定义的拦截器LogInterceptor到HelloWorld服务的入拦截器链中。
@InInterceptors(classes={LogInterceptor.class}, interceptors={"com.tiantian.cxftest.interceptor.LogInterceptor"})
@WebService
public interface HelloWorld {
public String sayHi(String who);
}
使用注解的方式配置其它拦截器的方式是类似的。使用注解在服务端的SEI上配置的拦截器会作用在服务端,如果客户端与服务端不在一起,需要单独在客户端上配置拦截器,也可以直接在客户端对应的SEI上通过上述四个注解进行配置,方法是一样的。
五、常用内置拦截器
日志拦截器:LoggingInInterceptor 入拦截器日志 LoggingOutInterceptor 出拦截器日志
参考:
CXF-API http://cxf.apache.org/javadoc/latest/
http://elim.iteye.com/blog/2248620#_Toc431737706
http://yufenfei.iteye.com/blog/1688133
http://blog.csdn.net/jaune161/article/details/25602655