1. 程式人生 > >Web Service學習之:CXF攔截器

Web Service學習之:CXF攔截器

一、用途

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

複製程式碼 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 {

//構造方法指定攔截器在什麼地方生效   //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 {

    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

    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 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"?>
<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" />
複製程式碼

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());  
}  

複製程式碼

<?xml version="1.0" encoding="UTF-8"?>
<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" />
複製程式碼 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 出攔截器日誌

參考: