关于webservice框架CXF的总结

  首先,先说一下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”

 

 

 

 

   

  从以上两图可以看到,客户端在接受完第一条返回值后就报了用户名密码错误的异常,服务端也是在接受完第一条数据后就报了异常,说明拦截器成功了。

  将客户端用户名密码改为正确的用户名密码后再次进行测试,结果正常。

 

 

 

 

posted @ 2019-09-04 16:51  iHADream  阅读(1844)  评论(0编辑  收藏  举报