一、用途
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