背景 当一个服务发布之后,只要有服务地址,我们就可以建立客户端对服务进行调用。如果服务的提供者想要对可以调用服务的客户端进行限制,如:只有某些客户可以调用此服务。这
背景
当一个服务发布之后,只要有服务地址,我们就可以建立客户端对服务进行调用。如果服务的提供者想要对可以调用服务的客户端进行限制,如:只有某些客户可以调用此服务。这时候就会用到拦截器,来进行权限控制。
明白了拦截器的应用场景,我们看看CXF的拦截器怎么用。
IN&OUT拦截器
从图中我们可以总结出,只要从一端发出消息时要进行拦截,就要使用OUT拦截器。如果要对接收的消息进行拦截就要使用IN拦截器。
在服务端添加IN拦截器和OUT拦截器
public class TestMain {
public static void main(String[] arg){
HelloWorld hw=new HelloWorldWS();
EndpointImpl eImpl=(EndpointImpl)Endpoint.publish("http://localhost:9009/HelloWorldWS", hw);
eImpl.getInInterceptors().add(new LoggingInInterceptor());
eImpl.getOutInterceptors().add(new LoggingOutInterceptor());
System.out.println("Web Service暴露成功!");
}
}
调用客户端,之后在服务端查看打印结果
Inbound Message
通过LoggingInInterceptor()拦截到的信息
六月 18, 2016 9:18:40 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
信息: Inbound Message
----------------------------
ID: 3
Address: http://localhost:9009/HelloWorldWS
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[185], content-type=[text/xml; charset=UTF-8], Host=[localhost:9009], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 2.4.0]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHi xmlns:ns2="http://ws.tgb.com/"><arg0>许晨阳</arg0></ns2:sayHi></soap:Body></soap:Envelope>
Outbound Message
通过LoggingOutInterceptor()拦截到的信息
六月 18, 2016 9:18:40 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
信息: Outbound Message
---------------------------
ID: 3
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHiResponse xmlns:ns2="http://ws.tgb.com/"><return>许晨阳,你好!现在时间是:2016-06-18 09:18:40</return></ns2:sayHiResponse></soap:Body></soap:Envelope>
在客户端添加IN拦截器和OUT拦截器
此时相同的信息会在客户端打印出来。
public class ClientMain {
public static void main(String[] args){
//工厂,继承Service
HelloWorldWS factory=new HelloWorldWS();
//只是服务的代理
HelloWorld hw=factory.getHelloWorldWSPort();
//客户端拦截器
Client c= ClientProxy.getClient(hw);
c.getInInterceptors().add(new LoggingInInterceptor());
c.getOutInterceptors().add(new LoggingOutInterceptor());
System.out.println(hw.sayHi("许晨阳"));
}
}
-------------------------------------- 六月 20, 2016 5:33:43 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log 信息: Outbound Message ---------------------------
ID: 1
Address: http://localhost:9009/HelloWorldWS
Encoding: UTF-8
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHi xmlns:ns2="http://ws.tgb.com/"><arg0>许晨阳</arg0></ns2:sayHi></soap:Body></soap:Envelope>
--------------------------------------
六月 20, 2016 5:33:43 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
信息: Inbound Message
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml;charset=UTF-8
Headers: {Content-Length=[252], content-type=[text/xml;charset=UTF-8], Server=[Jetty(7.3.1.v20110307)]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHiResponse xmlns:ns2="http://ws.tgb.com/"><return>许晨阳,你好!现在时间是:2016-06-20 05:33:43</return></ns2:sayHiResponse></soap:Body></soap:Envelope>
自定义拦截器
通过自定义拦截器,我们可以根据需求修改SOAP消息,进行权限控制。
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public AuthInterceptor(String phase) {
super(phase);
}
@Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers=soapMessage.getHeaders();
if(headers==null || headers.size()<1){
throw new Fault(new IllegalArgumentException("没有Header,不能调用"));
}
Header firstHeader=headers.get(0);
Element ele=(Element)firstHeader.getObject();
NodeList userIds=ele.getElementsByTagName("userId");
NodeList pwds=ele.getElementsByTagName("pwd");
if(userIds==null || userIds.getLength()!=1){
throw new Fault(new IllegalArgumentException("用户名格式不对!"));
}
if(pwds==null || pwds.getLength()!=1){
throw new Fault(new IllegalArgumentException("密码格式不对!"));
}
String userId=userIds.item(0).getTextContent();
String pwd=pwds.item(0).getTextContent();
if(!userId.equals("1") ||!pwd.equals("123")){
throw new Fault(new IllegalArgumentException("用户名和密码不对!"));
}
}
}
在这个拦截器中,首先判断是SOAP消息中是否有Header,如果有,再判断用户名和密码是否正确。
此时,要将自定义的拦截器添加到InInterceptors,并传入一个Phase的字符串参数。
public class TestMain {
public static void main(String[] arg){
HelloWorld hw=new HelloWorldWS();
//hw.sayHi("许晨阳");
EndpointImpl eImpl=(EndpointImpl)Endpoint.publish("http://localhost:9009/HelloWorldWS", hw);
eImpl.getInInterceptors().add(new LoggingInInterceptor());
eImpl.getOutInterceptors().add(new LoggingOutInterceptor());
//自定义拦截器
eImpl.getInInterceptors().add(new AuthInterceptor(Phase.PRE_INVOKE));
System.out.println("Web Service暴露成功!");
}
}
此时,如果客户端直接调用,会无法调用。
六月 20, 2016 5:51:24 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
信息: Inbound Message
----------------------------
ID: 1
Response-Code: 500
Encoding: UTF-8
Content-Type: text/xml;charset=UTF-8
Headers: {Content-Length=[222], content-type=[text/xml;charset=UTF-8], Server=[Jetty(7.3.1.v20110307)]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>没有Header,不能调用</faultstring></soap:Fault></soap:Body></soap:Envelope>
--------------------------------------
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: 没有Header,不能调用
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:146)
at com.sun.proxy.$Proxy23.sayHi(Unknown Source)
at com.tgb.ws.ClientMain.main(ClientMain.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: org.apache.cxf.binding.soap.SoapFault: 没有Header,不能调用
此时返回到客户端的SOAP消息为<soap:Fault>
。
所以我们要在客户端也添加拦截器将用户名和密码添加到SOAP消息的Header中。
public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String userId;
private String pwd;
public AddHeaderInterceptor(String userId, String pwd) {
super(Phase.PREPARE_SEND);
this.userId=userId;
this.pwd=pwd;
}
@Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers= soapMessage.getHeaders();
System.out.println(headers.size());
Document doc= DOMUtils.createDocument();
Element ele=doc.createElement("authHeader");
Element eleUser=doc.createElement("userId");
eleUser.setTextContent(userId);
Element elePwd=doc.createElement("pwd");
elePwd.setTextContent(pwd);
ele.appendChild(eleUser);
ele.appendChild(elePwd);
Header header=new Header(new QName("http://impl.ws.tgb.com/"),ele);
headers.add(header);
System.out.println(headers.size());
}
}
public class ClientMain {
public static void main(String[] args){
//工厂
HelloWorldWS factory=new HelloWorldWS();
//只是服务的代理
HelloWorld hw=factory.getHelloWorldWSPort();
//客户端拦截器
Client c= ClientProxy.getClient(hw);
c.getInInterceptors().add(new LoggingInInterceptor());
c.getOutInterceptors().add(new AddHeaderInterceptor("1","123"));
c.getOutInterceptors().add(new LoggingOutInterceptor());
System.out.println(hw.sayHi("许晨阳"));
}
}
需要注意的是,我们要把此拦截器添加到客户端的OutInterceptors,这个和服务端不一样。
总结
CXF拦截器可以让我们修改SOAP消息,并在此基础上对客户端进行权限控制,很方便。