Web Service學習-CXF開發Web Service的權限控制(二)
Web Service如何進行權限控制?
解決思路:服務器端要求input消息總是攜帶有用戶名,密碼信息,如果沒有用戶名和密碼信息,直接拒絕調用
解決方案:攔截器
為了讓程序員能訪問,并修改CXF框架所生成的SOAP消息,CXF提供了攔截器
CXF(Celtix +XFire)說明:
如果不用CXF等框架,SOAP消息的生成,解析都是由程序員負責。無論是添加用戶名,密碼信息還是提取用戶名,密碼信息,都可由程序員代碼完成。
如果使用CXF等框架,SOAP消息的生成,解析都是由CXF等框架來完成。
總的來說,CXF對發布WebService進行了封裝,簡化了我們的操作。
攔截器:
服務端添加攔截器:
1,獲取Endpoint的publish的方法返回值。
2,調用該對象的getInInterceptors,getOutInterceptors方法來獲取In,Out攔截器列表,接下來就可以添加攔截器了
package com.tgb.client; import javax.xml.ws.Endpoint; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.EndpointImpl; import com.tgb.service.HelloWorld; import com.tgb.service.impl.HelloWorldImpl; public class ServerMain { public static void main(String[] args){ HelloWorld hw=new HelloWorldImpl(); //調用endpoint的publish方法,來發布web service // Endpoint.publish("http://192.168.24.215:8889/hjy",hw); EndpointImpl ep=(EndpointImpl)Endpoint.publish("http://192.168.24.215:8899/hjy",hw); //添加In攔截器 ep.getInInterceptors().add(new LoggingInInterceptor()); //添加Out攔截器 ep.getOutInterceptors().add(new LoggingOutInterceptor()); System.out.println("Web Service暴露成功"); } }
輸出內容:

客戶端添加攔截器:
1,添加相應的CXF的jar包
2,調用ClientProxy的getClient方法,調用該方法以遠程Web Service的代理為參數
3,調用Client對象的getInInterceptors,getOutInterceptors方法來獲取In,Out攔截器列表,接下來就可以添加攔截器了
package hjy; import java.util.List; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import com.tgb.service.Cat; import com.tgb.service.HelloWorld; import com.tgb.service.User; import com.tgb.service.impl.HelloWorldImpl; public class ClientMain { public static void main(String[] args){ HelloWorldImpl factory=new HelloWorldImpl(); //此處返回的只是遠程Web Service的代理 HelloWorld hw=factory.getHelloWorldImplPort(); Client client=ClientProxy.getClient(hw); client.getInInterceptors().add(new LoggingInInterceptor()); client.getOutInterceptors().add(new LoggingOutInterceptor()); System.out.println(hw.sayHi("hejingyuan")); System.out.println("--------------------------"); User user=new User(); user.setId(20); user.setName("孫悟空"); user.setPass("111"); user.setAddress("花果山"); List<Cat> cats=hw.getCatsByUser(user); for(Cat cat:cats){ System.out.println(cat.getName()); } System.out.println("--------------------------"); System.out.println(hw.getAllCats().getEntry().get(0).getKey()); System.out.println(hw.getAllCats().getEntry().get(0).getValue().getName()); } }
打印內容為:

自定義攔截器
實現效果:當輸入用戶名密碼時,才可以調用我們的服務。即我們需要在服務端添加輸入攔截,在客戶端添加輸出攔截
自定義攔截器,需要實現Interceptor接口,實際上,我們一般會繼承AbstractPhaseInterceptor
服務端代碼:
package com.tgb.client; import javax.xml.ws.Endpoint; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.EndpointImpl; import com.tgb.auth.AuthInterceptor; import com.tgb.service.HelloWorld; import com.tgb.service.impl.HelloWorldImpl; public class ServerMain { public static void main(String[] args){ HelloWorld hw=new HelloWorldImpl(); //調用endpoint的publish方法,來發布web service // Endpoint.publish("http://192.168.24.215:8889/hjy",hw); EndpointImpl ep=(EndpointImpl)Endpoint.publish("http://192.168.24.215:8891/hjy",hw); //添加In攔截器,該AuthInterceptor就會負責檢查用戶名,密碼是否正確 ep.getInInterceptors().add(new AuthInterceptor()); //添加Out攔截器 // ep.getOutInterceptors().add(new LoggingOutInterceptor()); System.out.println("Web Service暴露成功"); } }
AuthInterceptor :
package com.tgb.auth; 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; //通過PhaseInterceptor,可以指定攔截器在哪個階段起作用 public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ //由于AbstractPhaseInterceptor無無參數構造器,使用繼承的方式,需要顯示調用父類有參數的構造器 public AuthInterceptor(){ //super表示顯示調用父類有參數的構造器 //顯示調用父類構造器之后,程序將不會隱式調用父類無參數的構造器 super(Phase.PRE_INVOKE);//該攔截器將會調用之前攔截SOAP消息 } //實現自己的攔截器時,需要實現handleMessage方法。 //handleMessage方法中的形參就是被攔截到的Soap消息 //一旦程序獲取了SOAP消息,剩下的事情就可以解析SOAP消息或修改SOAP消息 @Override public void handleMessage(SoapMessage msg) throws Fault { System.out.println("-------"+msg); //從這里可以看出,我們已經攔截到了SOAP消息 //得到SOAP消息所有Header List<Header> headers=msg.getHeaders(); //如果沒有Header if(headers==null||headers.size()<1){ throw new Fault(new IllegalArgumentException("根本沒有Header,不能調用")); } //假如要求第一個Header攜帶了用戶名,密碼信息 Header firstHeader=headers.get(0); Element ele=(Element)firstHeader.getObject(); NodeList userIds=ele.getElementsByTagName("userId"); NodeList userPasses=ele.getElementsByTagName("userPass"); if(userIds.getLength()!=1){ throw new Fault(new IllegalArgumentException("用戶名的格式不正確!")); } if(userPasses.getLength()!=1){ throw new Fault(new IllegalArgumentException("密碼的格式不正確!")); } //得到第一個userId元素里的文本內容,以該內容作為用戶名字 String userId=userIds.item(0).getTextContent(); String userPass=userPasses.item(0).getTextContent(); //實際項目中,應該去查詢數據庫,該用戶名密碼是否被授權調用web service if(!userId.equals("hejingyuan") || !userPass.equals("hjy")){ throw new Fault(new IllegalArgumentException("用戶名密碼不正確!")); } } }
客戶端代碼:
package hjy; import java.util.List; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import com.tgb.auth.AddHeaderInterceptor; import com.tgb.service.Cat; import com.tgb.service.HelloWorld; import com.tgb.service.User; import com.tgb.service.impl.HelloWorldImpl; public class ClientMain { public static void main(String[] args){ HelloWorldImpl factory=new HelloWorldImpl(); //此處返回的只是遠程Web Service的代理 HelloWorld hw=factory.getHelloWorldImplPort(); Client client=ClientProxy.getClient(hw); //參數為輸入的用戶名,密碼 client.getOutInterceptors().add(new AddHeaderInterceptor("hejingyuan","hjy")); System.out.println(hw.sayHi("hejingyuan")); System.out.println("--------------------------"); User user=new User(); user.setId(20); user.setName("孫悟空"); user.setPass("111"); user.setAddress("花果山"); List<Cat> cats=hw.getCatsByUser(user); for(Cat cat:cats){ System.out.println(cat.getName()); } System.out.println("--------------------------"); System.out.println(hw.getAllCats().getEntry().get(0).getKey()); System.out.println(hw.getAllCats().getEntry().get(0).getValue().getName()); } }
AddHeaderInterceptor:
package com.tgb.auth; 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; public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ private String userId; private String userPass; public AddHeaderInterceptor(String userId,String userPass){ super(Phase.PREPARE_SEND);//在準備發送SOAP消息時啟用該攔截器 this.userId=userId; this.userPass=userPass; } @Override public void handleMessage(SoapMessage msg) throws Fault { List<Header> headers=msg.getHeaders(); //創建Document對象 Document doc=DOMUtils.createDocument(); Element ele=doc.createElement("authHeader"); //此處創建的元素應該按照服務器那邊的要求 Element idEle=doc.createElement("userId"); idEle.setTextContent(userId); Element passEle=doc.createElement("userPass"); passEle.setTextContent(userPass); ele.appendChild(idEle); ele.appendChild(passEle); /** * 上面代碼生成了一個如下XML文檔片段 * <authHeader> * <userId>hejingyuan</userId> * <userPass>hjy</userPass> * </authHeader> */ //把ele元素包裝成Header,并添加到SOAP消息的Header列表中 headers.add(new Header(new QName("hejingyuan"),ele)); } }
啟動服務端的ServerMain的main函數,將服務發布,然后啟動客戶端ClientMain的main函數去訪問服務端提供的服務。
用戶名密碼錯誤時:

用戶名密碼正確時:
總結:
權限控制的實現方式為使用攔截器,對于攔截到的Soap消息進行修改。
SOAP消息:
根元素是Envolope
Header
默認情況下,Header元素不是強制出現的
Header元素由程序員控制添加,主要用戶攜帶一些額外的信息,比如用戶名,密碼信息
Body
如果調用正確,Body元素的內容應該遵守WSDL所要求的格式
如果調用錯誤,Body元素的內容就是Fault子元素
來自: http://blog.csdn.net/hejingyuan6/article/details/47133659