首先,先說一下webservice的優點吧,websevice最大的優點就是可以跨平台跨語言遠程調用,就是你在webservice平台上用java寫了一個api發布了,其他象用C++,paython寫的平台也能通過webservice調用你寫的api,同樣反過來也行的通。現在主流的webservice框架有Apache Axis1、Apache Axis2、Codehaus XFire、Apache CXF、sun JAX-WS(最簡單、方便)等等,我接觸到的就是Axis2和CXF,感覺CXF要比Axis2簡潔,方便使用一點,https://www.cnblogs.com/ruiati/p/6640287.html 這位老哥對現在較為流行的webservice框架進行了對比,有興趣的可以看看。好了接下來開始總結webservice知識和CXF框架。(因為樓主之前沒有接觸過webservice,所以會從最基本的開始)。
1.webservice組成
webservice由XML+XSD,SOAP和WSDL組成。
1.1 xml+xsd
webservice通過xml來發送數據,但是由於xml並不能明確定義數據格式,而不同平台之間的數據類型可能不一致,可能導致接受數據出現錯誤,所以,不同平台之間傳輸數據時需要統一數據規范,以統一的規范發送數據,再根據規范接受數據,這樣就解決了傳輸數據類型問題。
1.2 soap
SOAP(simple object access protocal)使用基於XML的數據結構和超文本傳輸協議(HTTP)來傳輸數據。SOAP協議 = HTTP協議 + XML數據格式。
1.3 wsdl
wsdl:Web Service Description Language,即網絡服務描述語言,用來告知使用或者准備使用的群體,該webservice接口的地址,需要傳輸什么內容,會返回什么內容,會調用那個方法等詳細信息。客戶端要調用一個WebService服務之前,要知道該服務的WSDL文件的地址。 WebService服務提供商可以通過兩種方式來暴露它的WSDL文件地址:1.注冊到UDDI服務器,以便被人查找;2.直接告訴給客戶端調用者。
1.4 SEI(WebService EndPoint Interface)
SEI是web service的終端接口,就是WebService服務器端用來處理請求的接口。
2.CXF框架
這里我們只是寫一下CXF發布服務和創建客戶端的大概流程,不整合其他框架,只使用CXF。
2.1創建服務端
首先,創建一個maven項目(省的手動導包),jdk最好用1.7的,然后新建一個接口和其實現類,添加一個方法,重點是在准備發布的接口和其實現類上添加@webservice注解,標明這是要發布的接口。



例如,我在接口中添加了兩個方法,一個直接返回信息,一個做判斷后返回。
然后,我們需要發布已經寫好的接口,首先我們要新建一個類,為方便理解命名為server。發布接口有兩種方式,一種為Javax.xml.ws包下的Endpoint,是java自帶的發布webservice的類,直接用該類的publish方法發布接口,發布的具體地址自己定義;另一種為org.apache.cxf.jaxws包下的JaxWsServerFactoryBean,是cxf的,可以設置要發布的地址,接口類,實現類,還可以添加輸入輸出攔截器,用攔截器可以實現接口的安全驗證,添加白名單等操作。

這樣服務端基本就配置完了,對了,還要在maven中添加依賴,具體依賴如下。
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.1.5</version>
</dependency>
然后我們啟動tomcat,在瀏覽器上粘貼上我們定義的地址,並在后邊添加?wsdl后綴,如果能夠正常打開,看到service名稱地址等,說明我們發布成功。

2.2 創建客戶端
在我們創建客戶端前,需要下載CXF框架,我們需要用到其中的wsdl2java工具,去CXF官網下載就可以,具體下載過程不再贅述,要注意的是下載完需要配置環境變量,將其添加到path中。
現在我們新建一個maven項目,為客戶端,我們創建客戶端需要根據服務端發布的wsdl來生成,所以,首先我們需要啟動之前的server端,然后在客戶端項目中找到要生成文件的位置,在命令行中打開要生成文件的位置,用wsdl工具生成所需文件。



若運行之后,如上圖所示,表示成功生成了客戶端代碼,生成文件如下圖所示。

接下來,新建一個類,名為client,在其中new一個webservice客戶端類,根據客戶端類生成接口類,根據接口類來調用我們所需方法。
public class Client { public static void main(String[] args) { HelloWorldServiceService helloworldServiceService =new HelloWorldServiceService(); HelloWorldService helloworldService = helloworldServiceService.getHelloWorldServicePort(); org.apache.cxf.endpoint.Client client = ClientProxy.getClient(helloworldService); client.getOutInterceptors().add(new AddHeaderInterceptor("lili","123"));//自定義輸出攔截器 client.getInInterceptors().add(new LoggingInInterceptor());//接收數據日志攔截器 client.getOutInterceptors().add(new LoggingOutInterceptor());//發送數據日志攔截器 // List<String> nums = new ArrayList<String>(); // nums.add("一號"); // nums.add("二號"); // nums.add("三號"); // nums.add("六號"); // for (String num:nums // ) { // System.out.println(helloworldService.hi(num)); // } List<User> users = new ArrayList<User>(); users.add(new User("麗麗","123")); users.add(new User("花花","123")); users.add(new User("菲菲","123")); for (User user:users ) { Authority au = helloworldService.judgAuthority(user); System.out.println(au); } } }
然后運行客戶端main方法即可看到運行結果。
這是client端的輸出,因為我們這是簡單測試,所以在server端方法的實現中直接寫死了驗證,用戶名必須是“麗麗”或者“菲菲”密碼都必須是123,而在client端我們第一個和第三個測試例子正確,第二個不正確,所以客戶端的返回如下圖。

這是server端輸出,server端我們讓他輸出他所接收到的內容。如下圖。

2.3 創建攔截器
攔截器是webservice進行驗證或過濾的重要方式,通過攔截器可以進行用戶名密碼驗證(當然,你也可以在接口內部實現用戶名密碼驗證,但是攔截器驗證實在接受數據之前進行了攔截驗證,更為安全嚴謹一些),ip驗證等等,也是webservice保證接口安全的重要方式。
CXF的攔截器主要有四種,客戶端的輸入攔截器,輸出攔截器,服務器端的輸入攔截器,輸出攔截器,它們的總體流程是這樣的,客戶端輸出攔截器——>服務端輸入攔截器——>服務端輸出攔截器——>客戶端輸入攔截器。
首先我們可以先添加日志攔截器,給客戶端服務端都加上輸入輸出攔截。主要方法為:getInInterceptor().add(要添加的攔截器),getOutInterceptor().add(要添加的攔截器)。


然后啟動服務端,運行客戶端,會出現以下內容:

上圖是客戶端的日志記錄,可以詳細看到客戶端發送接收數據的詳細情況,服務端同理,不再貼了。
現在我們定義一個驗證用戶名密碼的攔截器,分別在客戶端和服務端添加輸出攔截器和輸入攔截器。
首先在客戶端天界一個輸出攔截器,AddHeaderInterceptor,添加頭信息的攔截器,用來給發送的數據的頭部,添加用戶名密碼,方便服務端驗證。注意,實際操作中,怎么驗證,要服務端和客戶端雙方溝通,協商好通信驗證的內容方式。

public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private String userName; private String password; public AddHeaderInterceptor(String userName, String password) { super(Phase.PREPARE_SEND);//這里的PREPARE_SEND表示在准備發送之前進行攔截 this.userName=userName; this.password=password; } public void handleMessage(SoapMessage soapMessage) throws Fault { List<Header> headers = soapMessage.getHeaders(); //因為發送的數據是xml格式的,所以此處是通過添加節點的方式添加用戶名密碼 Document document = DOMUtils.createDocument(); Element element = document.createElement("Auth"); Element uElement = document.createElement("userName");//添加一個userName節點 Element pElement = document.createElement("password");//添加一個password節點 uElement.setTextContent(userName);//將用戶名填寫在節點中 pElement.setTextContent(password);//將密碼填寫在節點中 element.appendChild(uElement); element.appendChild(pElement); headers.add(new Header(new QName("auth"),element)); } }
客戶端添加完成之后,同理,我們在服務端添加進行驗證的攔截器。

public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage> { public MyInterceptor() { super(Phase.PRE_INVOKE);//調用方法前調用自定義攔截器 } public void handleMessage(SoapMessage soapMessage) throws Fault { List<Header> headers = soapMessage.getHeaders(); if (headers==null||headers.size()==0){ throw new Fault(new IllegalArgumentException("頭部為空,請填寫完整")); } //根據節點來讀取數據 Header header = headers.get(0); Element element = (Element) header.getObject(); NodeList uList = element.getElementsByTagName("userName"); NodeList pList = element.getElementsByTagName("password"); if (uList.getLength()!=1){ throw new Fault(new IllegalArgumentException("用戶名格式不正確")); } if (pList.getLength()!=1){ throw new Fault(new IllegalArgumentException("密碼格式不正確")); } String userName = uList.item(0).getTextContent(); String password = pList.item(0).getTextContent(); if (!"lili".equals(userName)||!"123".equals(password)){ throw new Fault(new IllegalArgumentException("用戶名密碼不正確")); } } }
為了方便,我們直接寫死用戶名密碼,接下來我們測試一下。
首先,測試一個錯誤的用戶名密碼,即用戶名不為“lili”或者密碼不為“123”,比如“feifei”,“123”


從以上兩圖可以看到,客戶端在接受完第一條返回值后就報了用戶名密碼錯誤的異常,服務端也是在接受完第一條數據后就報了異常,說明攔截器成功了。
將客戶端用戶名密碼改為正確的用戶名密碼后再次進行測試,結果正常。


