项目中有时候也会做一些类似于权限验证的东西,拦截器也是一种实现方式。拦截器主要作用是做一些权限过滤,编码处理等。
webService接口也可以上拦截器,我们也可以给webservice请求加权限判断功能;
webservice分服务端和客户端,服务端和客户端都是可以加拦截器的,无论是服务端还是客户端,都分进,出(In,Out)拦截器;
可以使用cxf内置拦截器,也可以自定义拦截器,无论是自定义的拦截器,还是CXF自带的拦截器,都必须实现Interceptor接口。
下面分别从这两个方面来讲解:
一、cxf内置拦截器
这里以日志拦截器为例:
服务端的Server类:
1 /** 2 * 3 */ 4 package com.hik.webservice; 5 6 import javax.xml.ws.Endpoint; 7 8 import org.apache.cxf.interceptor.LoggingInInterceptor; 9 import org.apache.cxf.interceptor.LoggingOutInterceptor; 10 import org.apache.cxf.jaxws.JaxWsServerFactoryBean; 11 12 import com.hik.webservice.impl.HelloWorldImpl; 13 14 /** 15 * @ClassName: Server 16 * @Description: TODO 17 * @author jed 18 * @date 2017年7月30日上午10:26:16 19 * 20 */ 21 public class Server { 22 23 public static void main(String[] args) { 24 System.out.println("web Service start"); 25 HelloWorldImpl implementor = new HelloWorldImpl(); 26 String address="http://192.168.0.102/helloWorld"; 27 //Endpoint.publish(address, implementor);//JDK实现 28 JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean(); 29 factoryBean.setAddress(address); //设置暴露地址 30 factoryBean.setServiceClass(HelloWorld.class); //接口类 31 factoryBean.setServiceBean(implementor); //设置实现类 32 factoryBean.getInInterceptors().add(new LoggingInInterceptor()); //添加in拦截器 日志拦截器 33 factoryBean.getOutInterceptors().add(new LoggingOutInterceptor()); // 添加out拦截器 34 factoryBean.create(); 35 System.out.println("web Service started"); 36 37 } 38 }
通过factoryBean对象可以获取拦截器组,添加进或者出拦截器。日志拦截器是经典的拦截器,开发经常用到。
我们可以把客户端的请求,以及服务端返回的信息打印出来,可以打印控制台,也可以打印到执行文件;这里为了演示方便,直接搞无参的拦截器,
打印到控制台;
执行下Server类:
再来执行下客户端的Client类,结果:
我们可以看到服务端server控制台有日志输出,仔细观察Server端的控制台:
1 八月 05, 2017 9:21:35 上午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld 2 信息: Inbound Message 3 ---------------------------- 4 ID: 1 5 Address: http://192.168.0.102/helloWorld?wsdl 6 Http-Method: GET 7 Content-Type: 8 Headers: {Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2], connection=[keep-alive], Content-Type=[null], Host=[192.168.0.102], User-Agent=[Java/1.7.0_79]} 9 -------------------------------------- 10 八月 05, 2017 9:21:36 上午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld 11 信息: Inbound Message 12 ---------------------------- 13 ID: 2 14 Address: http://192.168.0.102/helloWorld 15 Encoding: UTF-8 16 Http-Method: POST 17 Content-Type: text/xml; charset=UTF-8 18 Headers: {Accept=[text/xml, multipart/related], connection=[keep-alive], Content-Length=[170], content-type=[text/xml; charset=UTF-8], Host=[192.168.0.102], SOAPAction=[""], User-Agent=[JAX-WS RI 2.2.4-b01]} 19 Payload: <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:getRoles xmlns:ns2="http://webservice.hik.com/"/></S:Body></S:Envelope> 20 -------------------------------------- 21 八月 05, 2017 9:21:37 上午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld 22 信息: Outbound Message 23 --------------------------- 24 ID: 2 25 Response-Code: 200 26 Encoding: UTF-8 27 Content-Type: text/xml 28 Headers: {} 29 Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getRolesResponse xmlns:ns2="http://webservice.hik.com/"><return><item><key>jack</key><value><id>3</id><roleName>程序员</roleName></value></item><item><key>admin</key><value><id>1</id><roleName>技术总监</roleName></value><value><id>2</id><roleName>产品经理</roleName></value></item></return></ns2:getRolesResponse></soap:Body></soap:Envelope> 30 --------------------------------------
这里的打印出来的就是日志信息:Inbound 进信息 Outbound 是出信息,进的时候,大家会看到有个Headers SOAP消息。后面我们可以在里面加我们的数据;
在Outbound Message里,Payload消息里我们可以找到webservice返回的数据 SOAP消息;
客户端也可以加进出拦截器,修改Client代码:
我们用到了ClientProxy,客户端代理
请求的时候,可以看到控制台的日志信息:(日志和服务端一样)
1 八月 05, 2017 10:05:22 上午 org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL 2 信息: Creating Service {http://webservice.hik.com/}HelloWorldService from WSDL: http://192.168.0.102/helloWorld?wsdl 3 八月 05, 2017 10:05:22 上午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld 4 信息: Outbound Message 5 --------------------------- 6 ID: 1 7 Address: http://192.168.0.102/helloWorld 8 Encoding: UTF-8 9 Http-Method: POST 10 Content-Type: text/xml 11 Headers: {Accept=[*/*], SOAPAction=[""]} 12 Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getRoles xmlns:ns2="http://webservice.hik.com/"/></soap:Body></soap:Envelope> 13 -------------------------------------- 14 八月 05, 2017 10:05:23 上午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld 15 信息: Inbound Message 16 ---------------------------- 17 ID: 1 18 Response-Code: 200 19 Encoding: UTF-8 20 Content-Type: text/xml; charset=UTF-8 21 Headers: {content-type=[text/xml; charset=UTF-8], Date=[Sat, 05 Aug 2017 02:05:22 GMT], Server=[Jetty(9.2.15.v20160210)], transfer-encoding=[chunked]} 22 Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getRolesResponse xmlns:ns2="http://webservice.hik.com/"><return><item><key>jack</key><value><id>3</id><roleName>程序员</roleName></value></item><item><key>admin</key><value><id>1</id><roleName>技术总监</roleName></value><value><id>2</id><roleName>产品经理</roleName></value></item></return></ns2:getRolesResponse></soap:Body></soap:Envelope> 23 -------------------------------------- 24 jack: 25 3,程序员 26 27 admin: 28 1,技术总监 29 2,产品经理
二、自定义拦截器
根据特殊需求,可能需自定义拦截器。如:客户端访问服务端webservice接口要加权限认证。
实现思路:
我们可以通过在SOAP消息的Header头信息中添加自定义信息,然后发送到服务端端,服务器端通过获取
Header头消息,然后进行认证;这里的添加消息,和获取消息认证,我们都是通过自定义拦截器来实现;
首先是服务器端:
我们自定义拦截器:MyInterceptor 继承AbstractPhaseInterceptor (最终也是实现Interceptor接口)即可。实现handleMessage方法即可。
自定义实现的拦截器:我们主要是获取Header头消息,然后获取userName和password节点,然后获取值,进行权限判断,假如认证不通过,我们抛出异常;
1 /** 2 * 3 */ 4 package com.hik.interceptor; 5 6 7 import java.util.List; 8 9 import org.apache.cxf.binding.soap.SoapMessage; 10 import org.apache.cxf.headers.Header; 11 import org.apache.cxf.interceptor.Fault; 12 import org.apache.cxf.phase.AbstractPhaseInterceptor; 13 import org.apache.cxf.phase.Phase; 14 import org.w3c.dom.Element; 15 import org.w3c.dom.NodeList; 16 17 /** 18 * @ClassName: MyInterceptor 19 * @Description: 自定义拦截器 20 * @author jed 21 * @date 2017年8月5日上午10:20:09 22 * 23 */ 24 public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ 25 /** 26 * 27 */ 28 public MyInterceptor() { 29 // 在调用方法之前调用拦截器 30 super(Phase.PRE_INVOKE); 31 } 32 33 public void handleMessage(SoapMessage message) throws Fault { 34 List<Header> heads = message.getHeaders(); 35 if(heads==null|| heads.size()==0){ 36 throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截")); 37 } 38 Header firstHeader = heads.get(0); 39 Element element = (Element) firstHeader.getObject(); 40 NodeList userIds = element.getElementsByTagName("userName"); 41 NodeList userPasses = element.getElementsByTagName("password"); 42 if(userIds.getLength()!=1){ 43 throw new Fault(new IllegalArgumentException("用户名格式不对")); 44 } 45 if(userPasses.getLength()!=1){ 46 throw new Fault(new IllegalArgumentException("密码格式不对")); 47 } 48 49 String userId = userIds.item(0).getTextContent(); 50 String userPass = userPasses.item(0).getTextContent(); 51 if(!userId.equals("admin")|| !userPass.equals("12345")){ 52 throw new Fault(new IllegalArgumentException("用户名或者密码不正确")); 53 } 54 } 55 56 }
在Server类里,我们要添加一个in 拦截器,在进入的时候,我们要进行验证;
1 /** 2 * 3 */ 4 package com.hik.webservice; 5 6 import javax.xml.ws.Endpoint; 7 8 import org.apache.cxf.interceptor.LoggingInInterceptor; 9 import org.apache.cxf.interceptor.LoggingOutInterceptor; 10 import org.apache.cxf.jaxws.JaxWsServerFactoryBean; 11 12 import com.hik.interceptor.MyInterceptor; 13 import com.hik.webservice.impl.HelloWorldImpl; 14 15 /** 16 * @ClassName: Server 17 * @Description: TODO 18 * @author jed 19 * @date 2017年7月30日上午10:26:16 20 * 21 */ 22 public class Server { 23 24 public static void main(String[] args) { 25 System.out.println("web Service start"); 26 HelloWorldImpl implementor = new HelloWorldImpl(); 27 String address="http://192.168.0.102/helloWorld"; 28 //Endpoint.publish(address, implementor);//JDK实现 29 JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean(); 30 factoryBean.setAddress(address); //设置暴露地址 31 factoryBean.setServiceClass(HelloWorld.class); //接口类 32 factoryBean.setServiceBean(implementor); //设置实现类 33 factoryBean.getInInterceptors().add(new LoggingInInterceptor()); //添加in拦截器 日志拦截器 34 factoryBean.getOutInterceptors().add(new LoggingOutInterceptor()); // 添加out拦截器 35 36 factoryBean.getInInterceptors().add(new MyInterceptor());// 添加自定义拦截器 37 factoryBean.create(); 38 System.out.println("web Service started"); 39 40 } 41 }
客户端代码:
我们同样要添加一个自定义拦截器:AddHeaderInterceptor,主要是在拦截器里创建头消息;请求服务器端传入头信息为服务器接收验证。
1 /** 2 * 3 */ 4 package com.hik.interceptor; 5 6 import java.util.List; 7 8 import javax.xml.namespace.QName; 9 10 import org.apache.cxf.binding.soap.SoapMessage; 11 import org.apache.cxf.headers.Header; 12 import org.apache.cxf.helpers.DOMUtils; 13 import org.apache.cxf.interceptor.Fault; 14 import org.apache.cxf.phase.AbstractPhaseInterceptor; 15 import org.apache.cxf.phase.Phase; 16 import org.w3c.dom.Document; 17 import org.w3c.dom.Element; 18 19 20 /** 21 * @ClassName: AddHeaderInterceptor 22 * @Description: TODO 23 * @author jed 24 * @date 2017年8月5日上午10:59:13 25 * 26 */ 27 public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ 28 29 private String userName; 30 private String password; 31 32 /** 33 * @param userName 34 * @param password 35 */ 36 public AddHeaderInterceptor(String userName, String password) { 37 super(Phase.PREPARE_SEND); // 发送SOAP消息之前调用拦截器 38 this.userName = userName; 39 this.password = password; 40 } 41 42 public void handleMessage(SoapMessage message) throws Fault { 43 List<Header> heads = message.getHeaders(); 44 Document doc = DOMUtils.createDocument(); 45 Element ele = doc.createElement("authHeader"); 46 Element idElement = doc.createElement("userName"); 47 idElement.setTextContent(userName); 48 Element passElement = doc.createElement("password"); 49 passElement.setTextContent(password); 50 ele.appendChild(idElement); 51 ele.appendChild(passElement); 52 53 heads.add(new Header(new QName("admin"), ele)); 54 } 55 56 57 }
Client类里我们要修改下,加下Out 拦截器:
1 /** 2 * 3 */ 4 package com.hik.webservice; 5 import java.util.List; 6 7 import org.apache.cxf.frontend.ClientProxy; 8 import org.apache.cxf.interceptor.LoggingInInterceptor; 9 import org.apache.cxf.interceptor.LoggingOutInterceptor; 10 11 import com.hik.interceptor.AddHeaderInterceptor; 12 /** 13 * @ClassName: Client 14 * @Description: TODO 15 * @author jed 16 * @date 2017年7月30日下午1:58:36 17 * 18 */ 19 public class Client { 20 21 public static void main(String[] args) { 22 HelloWorldService service = new HelloWorldService(); 23 HelloWorld helloWorld = service.getHelloWorldPort(); //代理 24 org.apache.cxf.endpoint.Client client =ClientProxy.getClient(helloWorld); 25 //client.getInInterceptors().add(new LoggingInInterceptor()); // 添加in拦截器 日志拦截器 26 client.getOutInterceptors().add(new AddHeaderInterceptor("admin", "12345")); // 添加自定义拦截器 27 client.getOutInterceptors().add(new LoggingOutInterceptor()); // 添加out拦截器 28 //org.apache.cxf.frontend.ClientProxy 29 //System.out.println(helloWorld.say("你好!")); 30 /*User user = new User(); 31 user.setUserName("lili"); 32 user.setPassword("123456"); 33 List<Role> roleList = helloWorld.getRoleByUser(user); 34 for(Role role : roleList){ 35 System.out.println(role.getId()+" , "+role.getRoleName()); 36 }*/ 37 MyRoleArray array = helloWorld.getRoles(); 38 List<MyRole> roleList = array.item; 39 for(int i=0;i<roleList.size();i++){ 40 MyRole mr = roleList.get(i); 41 System.out.println(mr.key+":"); 42 for(Role r: mr.getValue()){ 43 System.out.println(r.getId()+","+r.getRoleName()+" "); 44 } 45 System.out.println(); 46 } 47 } 48 }
这样就完整了自定义拦截器实现权限认证;先运行Server类,和以前一样;
客户端日志打印:
服务端日志信息:
假如我们把 client.getOutInterceptors().add(new AddHeaderInterceptor("admin","123")); // 添加自定义拦截器
密码改成 123
然后运行Client类,会报错;用户名密码不正确。