5、拦截器(WebService笔记)

一、CXF拦截器

为了让程序员能访问、并修改CXF框架所生成的SOAP消息,CXF提供了拦截器。
拦截器可以加载服务器端,也可以加载客户端,而拦截器分为In拦截器和Out拦截器。

1.1、对于在服务器端添加拦截器(Inter_Server工程)

MyService.java

package org.fkjava.cxf.ws.server;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyService {
    public static void main(String[] args) throws IOException {

        HelloWorld hw = new HelloWorldWs();

        //调用此方法发布WebService
        EndpointImpl ep = (EndpointImpl)Endpoint.publish("http://localhost:8080/myService", hw);
        ep.getInInterceptors().add(new LoggingInInterceptor(new PrintWriter(new FileWriter("in.txt"))));//添加In拦截器
        ep.getOutInterceptors().add(new LoggingOutInterceptor(new PrintWriter(new FileWriter("out.txt"))));//添加Out拦截器
        System.out.println("Hello World!");
    }
}

说明:

  • (1)首先获取Endpointpublish方法的返回值;
  • (2)调用该方法的返回值的getInInterceptorgetOutInterceptor方法来获取In、Out拦截器列表,接下来就可以添加拦截器了。
  • (3)这里我们是使用的CXF给我们定义好的拦截器,但是这个拦截器在API文档中是查不到的,我们将相关输入和输出信息输出到文件中。添加的拦截器中(如LoggingInInterceptor)如果不给参数,则相关数据打印在控制台,这里我们让其输出到相关的文件中去。

注意:这里如果不想太麻烦,直接加入所有的依赖包。同时客户端不需要改变,因为我们需要的操作没有改变。

1.2 测试

这里我们使用工程WS_Client03对上面才改造服务端进行测试。在输出的文件中我们可以看到一些信息。在测试方法中我们总共使用的3个操作,于是应该有6个SOAP消息,从输出的文件中我们确实可以看到6个SOAP消息,在in.txt文件中前面两条先不管。我们从中截取出一段出来进行分析:
in.txt


    
        
            张三
        
    

out.txt


    
        
            张三您好!现在的时间是: Mon Jun 27 21:18:11 CST 2016
        
    

说明:从这里我们可以看出SOAP消息的结构

  • SOAP信息的根元素是Envelope,此元素包含两个子元素:Header(默认情况下没有,即不是强制出现的,其是由程序员控制添加,主要用于携带一些额外的信息,如用户名和密码)和Body
  • 对于Body子元素:(1)如果调用正确,其内容应该遵守wsdl所要求的格式;(2)如果调用错误(比如我们使用http://localhost:8080/myService调用,不指明服务名),其内容就是Fault子元素。

1.3 在客户端添加拦截器(工程Inter_Client

客户端的做法和服务端的做法基本一样。
MyClient.java

package org.fkjava.cxf.ws.client;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.fkjava.cxf.ws.Cat;
import org.fkjava.cxf.ws.Entry;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.StringCat;
import org.fkjava.cxf.ws.User;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyClient {
    public static void main(String[] args) throws IOException {
        HelloWorldWs factory = new HelloWorldWs();
        HelloWorld hw = factory.getHelloWorldWsPort();
        
        Client client = ClientProxy.getClient(hw) ;//调用此方法,以远程WebService的代理为参数
        client.getInInterceptors().add(new LoggingInInterceptor(new PrintWriter(new FileWriter("in.txt"))));
        client.getOutInterceptors().add(new LoggingOutInterceptor(new PrintWriter(new FileWriter("out.txt"))));
        System.out.println(hw.sayHi("张三"));
        
        User user = new User();
        user.setId(30);//只要名字和密码相同则认为是同一个用户,所以这里给30没关系
        user.setName("大熊");
        user.setPassword("111");
        
        List cats = hw.getCatsByUser(user);
        for(Cat cat : cats){
            System.out.println(cat.getName());
        }
        
        StringCat sc = hw.getAllCats();
        for(Entry entry : sc.getEntries()){
            System.out.println(entry.getKey() + entry.getValue().getName());
            
        }
    }
}

说明:首先我们使用ClientProxy类得到Client,之后就可以通过此类得到相关的拦截器列表了。同样测试之后我们也可以在相关的输出文件中看到输出的SOAP消息。

二、自定义拦截器(工程Auth_Server

2.1服务端

这里我们是要自定义一个拦截器来进行用户名和密码的检查,于是只需要在服务器端加一个In拦截器即可。

MyServer.java

package org.fkjava.cxf.ws.server;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.auth.AuthInterceptor;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyServer {
    public static void main(String[] args) throws IOException {

        HelloWorld hw = new HelloWorldWs();

        //添加一个自定义的In拦截器,负责检查用户和密码
        EndpointImpl ep = (EndpointImpl)Endpoint.publish("http://localhost:8080/myService", hw);
        ep.getInInterceptors().add(new AuthInterceptor());//添加In拦截器
        System.out.println("Hello World!");

    }
}

说明:这里的AuthInterceptor就是我们自定义的拦截器。我们需要实现Interceptor接口,而实际上,我们一般会继承AbstractPhaseInterceptor
AuthInterceptor.java

package org.fkjava.cxf.ws.auth;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
//通过PhaseInterceptor指定拦截器在哪个阶段起作用
public class AuthInterceptor extends AbstractPhaseInterceptor {
    
    public AuthInterceptor() {
        //下面的常量表示在调用之前让拦截器起作用
        super(Phase.PRE_INVOKE);//显式调用父类有参构造器,因为AbstractPhaseInterceptor没有无参构造器
    }

    //实现自己的拦截器的时候需要实现此方法,其中的形参就是被拦截到的SOAP消息
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        System.out.println("********" + msg);//从这里可以看到已经拦截到了SOAP消息
    }
}

说明:这里我们需要实现handleMessage方法,同时由于AbstractPhaseInterceptor抽象类没有无参构造函数,所以我们必须显式调用有参构造函数。其中参数表示让拦截器起作用的阶段,这里是调用之前拦截。拦截到的就是SOAP消息,之后我们需要修改和解析消息,这里我们先验证是否拦截到了SOAP消息,我们可以使用客户端进行访问来测试。会打印出相应的的消息。

解析SOAP消息:
AuthInterceptor.java

package org.fkjava.cxf.ws.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 {
    
    public AuthInterceptor() {
        //下面的常量表示在调用之前让拦截器起作用
        super(Phase.PRE_INVOKE);//显式调用父类有参构造器,因为AbstractPhaseInterceptor没有无参构造器
    }

    //实现自己的拦截器的时候需要实现此方法,其中的形参就是被拦截到的SOAP消息
    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        System.out.println("********" + msg);//从这里可以看到已经拦截到了SOAP消息
        List
headers = msg.getHeaders();//得到SOAP的所有HEADER //如果没有HEADER if(headers == null || headers.size() < 1){ throw new Fault(new IllegalArgumentException("没有头信息")); } //加入要求第一个HEADER中携带了用户名和密码信息 Header firstHeader = headers.get(0);//得到第一个HEADER Element ele = (Element) firstHeader.getObject();//得到HEADER的内容 NodeList usernames = (NodeList) ele.getElementsByTagName("username");//我们要求有一个username的标签 NodeList passwords = (NodeList) ele.getElementsByTagName("password"); if(usernames.getLength() != 1){ throw new Fault(new IllegalArgumentException("用户名的格式不对")); } if(passwords.getLength() != 1){ throw new Fault(new IllegalArgumentException("密码的格式不对")); } String username = usernames.item(0).getTextContent();//得到第一个userId元素里的文本内容,以该内容作为用户名 String password = passwords.item(0).getTextContent(); //实际项目中应该去查询数据库,检查该用户名密码是否能够被授权 if(!(username.equals("大熊") && password.equals("111"))){ throw new Fault(new IllegalArgumentException("用户名或密码不正确")); } //放行。。。这里不需要我们管 } }

说明:

  • 1.首先我们得到一个Header列表。然后进行判断,如果确实有Header元素则我们取出列表中的第一个Header,因为会有很多Header,而这里其实只有一个,但是我们认为我们的用户名和密码信息放在第一个Header中。
  • 2.下面我们需要从头中解析出用户名和密码,当然用户名和密码信息所在的元素肯定是和客户端设置的一致,解析出来判断之后我们只需放行即可。

2.2 客户端(工程Auth_Client

MyClient.java

package org.fkjava.cxf.ws.client;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.fkjava.cxf.ws.Cat;
import org.fkjava.cxf.ws.Entry;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.StringCat;
import org.fkjava.cxf.ws.User;
import org.fkjava.cxf.ws.auth.AddHeaderInterceptor;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyClient {
    public static void main(String[] args) throws IOException {
        HelloWorldWs factory = new HelloWorldWs();
        HelloWorld hw = factory.getHelloWorldWsPort();
        
        Client client = ClientProxy.getClient(hw) ;//调用此方法,以远程WebService的代理为参数
        client.getOutInterceptors().add(new AddHeaderInterceptor("大熊", "111"));
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        System.out.println(hw.sayHi("张三"));
        
        User user = new User();
        user.setId(30);//只要名字和密码相同则认为是同一个用户,所以这里给30没关系
        user.setName("大熊");
        user.setPassword("111");
        
        List cats = hw.getCatsByUser(user);
        for(Cat cat : cats){
            System.out.println(cat.getName());
        }
        
        StringCat sc = hw.getAllCats();
        for(Entry entry : sc.getEntries()){
            System.out.println(entry.getKey() + entry.getValue().getName());
            
        }
    }
}

说明:客户端我们是加一个Out拦截器,即在输出SOAP消息的时候使用拦截器加上用户名和密码等信息头,这里我们还使用了一个CXF提供的拦截器主要是为了便于通过控制台查看一些信息。

自定义拦截器:
AddHeaderInterceptor.java

package org.fkjava.cxf.ws.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 {
    
    private String username ;
    private String password;
    
    public AddHeaderInterceptor(String username, String password) {
        super(Phase.PREPARE_SEND);//这里表示准备发送SOAP消息的时候调用此拦截器
        this.username = username;
        this.password = password;
        
    }

    @Override
    public void handleMessage(SoapMessage msg) throws Fault {
        List
headers = msg.getHeaders(); Document document = DOMUtils.createDocument();//创建一个Document对象 Element ele = document.createElement("authHeader");//创建一个元素,这个名字随便 Element usernameEle = document.createElement("username");//创建一个元素,注意和服务端元素名字一致 usernameEle.setTextContent(username);//将相关的值设置进去 Element passwordEle = document.createElement("password");//创建一个元素 passwordEle.setTextContent(password); ele.appendChild(usernameEle); ele.appendChild(passwordEle); //生成了一个如下的代码片段 /* * username * password * * */ //把ele元素包装成Header类,然后添加到SOAP消息的Header列表中 headers.add(new Header(new QName("fkjava"), ele));//这里的QName参数值随便设置 } }

说明:当然拦截器中我们肯定要将用户名和密码传递进去。和服务端的实现方式是一样的,只是客户端是需要添加xml代码片段。测试之后我们发现在控制台可以看到这样的信息:


    
        
            大熊
            111
        
    
    
        
            张三
        
    

说明:这样便成功了。而中的内容不受我们控制,是由WSDL决定的。

你可能感兴趣的:(5、拦截器(WebService笔记))