CXF的攔截器和以前學過的servlet的攔截器類似的,都是在開始或結束切入一段代碼,執行一些邏輯之類的。我們可以在調用ws服務前設置攔截器,也可以在調用ws服務後設置攔截器,當然了,攔截器也可以添加多個,CXF中有自己內置的攔截器,先來寫個簡單CXF自帶的攔截器實例熟悉一下在CXF中如何添加,然後再來自定義CXF攔截器。
1. CXF內置的攔截器設置
還是使用上一節的ws,在原來的基礎上添加以下攔截器,如下:
啓動之後,客戶端訪問一下,看服務端控制檯的輸出(爲了清楚點,我就不縮小了):
可以看到,請求的時候會被攔截器攔截,請求結束也會被攔截,從打印的日誌消息可以看出,發送的是soap消息,返回的數據由於截屏的範圍我就不截取了,這是在服務端添加的攔截器。
那客戶端如何添加攔截器呢?由於client端無法直接獲取攔截器組,所以我們需要首先獲取一個client的代理,然後通過這個代理來獲取攔截器組,如下:
客戶端訪問一下,看下客戶端控制檯的輸出結果(爲了清楚點,我就不縮小了):
可以看出,客戶端如果設置攔截器的話,也會打印出日誌消息,而且客戶端和服務端的攔截器執行順序剛好相反。這就是CXF內置的攔截器,下面我們來自定義CXF的攔截器。
2. 自定義CXF攔截器
自定義攔截器的話,我們來弄個需求,使用攔截器進行權限的認證。自定義攔截器需要繼承AbstractPhaseInterceptor<SoapMessage>
,其中SoapMessage是用來封裝soap消息的,我們具體來看下如何自定義CXF攔截器,首先看服務端,在上面的代碼的定義的兩個內置攔截器下面添加一個自定義攔截器即可:
factoryBean.getInInterceptors().add(new MyInterceptor());
- 1
然後重點是這個MyInterceptor,如下:
public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public MyInterceptor() {
super(Phase.PRE_INVOKE); //在調用方法之前調用自定義攔截器
}
public void handleMessage(SoapMessage message) throws Fault {
List<Header> headers = message.getHeaders(); //根據soap消息獲取頭部
if(headers == null && headers.size() == 0) {
throw new Fault(new IllegalArgumentException("沒有Header,攔截器實施攔截"));
}
Header firstHeader = headers.get(0); //我們等會只傳一個頭部過來
Element elm = (Element) firstHeader.getObject();//將該頭部轉成一個Element對象
NodeList userList = elm.getElementsByTagName("username"); //根據標籤獲取值
NodeList pwdList = elm.getElementsByTagName("password");
// 進行身份認證
if(userList.getLength() != 1) {//只有一個用戶
throw new Fault(new IllegalArgumentException("用戶名格式不對"));
}
if(pwdList.getLength() != 1) {//只有一個密碼
throw new Fault(new IllegalArgumentException("密碼格式不對"));
}
String username = userList.item(0).getTextContent(); //因爲就一個,所以獲取第一個即可
String password= pwdList.item(0).getTextContent();
if(!username.equals("admin") || !password.equals("123")) {
throw new Fault(new IllegalArgumentException("用戶名或者密碼錯誤"));
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
上面的代碼邏輯很簡單,等會兒客戶端會傳過來一個soap消息,我們會將用戶名和密碼封裝到頭部中傳過來,那麼在這邊,通過解析soap消息中頭部的數據,來進行身份認證。所以接下來完成客戶端那邊的攔截器。
客戶端這邊要自定義一個out攔截器了,因爲這邊是發送數據,同上,首先在原來客戶端定義的兩個內置CXF攔截器上面添加一個自定義攔截器,如下:
client.getOutInterceptors().add(new AddHeaderInterceptor("admin", "123"));//添加自定義攔截器
- 1
在自定義攔截器中,將用戶名和密碼傳進去,重點來看一下這個自定義攔截器,如下:
public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String username;
private String password;
public AddHeaderInterceptor(String username, String password) {
super(Phase.PREPARE_SEND); //準備發送soap消息的時候調用攔截器
this.username = username;
this.password = password;
}
public void handleMessage(SoapMessage message) throws Fault {
List<Header> headerList = message.getHeaders();
Document doc = DOMUtils.createDocument();
// 定義三個對象
Element elm = doc.createElement("authHeader");
Element userElm = doc.createElement("username");
Element pwdElm = doc.createElement("password");
// 給用戶名和密碼對象賦值
userElm.setTextContent(username);
pwdElm.setTextContent(password);
// 將用戶名和密碼的對象添加到elm中
elm.appendChild(userElm);
elm.appendChild(pwdElm);
headerList.add(new Header(new QName("head"), elm));//往soap消息頭部中添加這個elm元素
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
從上面的代碼中可以看出,首先通過構造函數將用戶名和密碼傳進去,然後獲取將要發送的soap消息的頭部,緊接着認爲構造出幾個元素,將用戶名和密碼封裝到元素中去,並放到soap消息的頭部,這樣等會soap消息就會攜帶這個用戶名和密碼的消息了,這樣就能在上面的服務端取出,進行身份認證了,這樣就前後連通了起來。測試結果我就不貼了,可以查看控制檯打印的結果,重點看一下soap消息,裏面封裝好了一個DOM對象,封裝了用戶名和密碼