Spring4.3.10 集成 Apache CXF 3.3 详细说明

目录

环境说明

服务端

导入 jar 包

源码展示

配置文件

浏览器访问测试

客户端

wsdl2java 生成代理类

源码内容

浏览器访问测试

配置拦截器

服务端

客户端

访问测试


环境说明

1、本文使用 Spring4.3.10 集成 Apache CXF 3.3 。

2、亲测 Java JDK 1.8 + Tomcat 9 集成成功(Tomcat 8 时集成失败了),采用手动导入 jar 包的方式,不使用 Maven。

3、将新建两个 Java Web 项目,web_service_spring_s 作为服务端,web_service_spring_c 作为客户端 。

4、使用 IDEA 14 开发。

5、如上所示是已经建好的项目,且运行成功之后的样子,现在解释如下;

beans.xml:spring 核心配置文件,CXF 也会在其中进行配置

springMVC.xml:springMVC 核心配置文件,为了尽可能的和实际项目一致,这里使用 MVC,方便从浏览器访问

UserController:用户控制器层,浏览器访问其中地址,如:http://localhost/web_service_spring_s/getUserById.action

User:用户实体类

UserServiceImpl:UserService 服务接口的实现类

UserService :服务接口,即是服务端的服务层,也会标识为 @WebService 作为 webService 服务接口

web.xml:Java web 应用入口文件,整合其它的配置文件。

index.jsp:默认的主页

服务端

导入 jar 包

1、这里只是演示 Spring 集成  CXF,不使用数据库操作,所以不导入操作数据库的 jar 包

2、官网下载的  Spring4.3.10 lib 目录下一共有 62 个 jar 包,官网下载的 CXF 的 lib 下一共有 182 个 Jar 包,显然全部导入不太现实,因为很多功能根本没有使用。

3、本人采用的方式是先导入 Spring4.3.10 的包,然后配置 beans.xml、springMVC.xml,web.xml,让项目能正常跑起来之后,再导入 CXF 的 jar,再修改配置文件进行整合。

4、使用 Spring 且不操作数据库时,只导入以下的 jar 包即可,下载地址:

链接:https://pan.baidu.com/s/1roPyjx3kPCXIH3sMn5f7pw    提取码:ybof 
链接:https://pan.baidu.com/s/1fRjr5U9z22SeN5G0LTJxXw     提取码:oll7 

5、当 Spring 可以使用之后,可以再导入 CXF 的 jar包,只要把《Apache CXF 入门第一个示例》中的 Jar 包追加即可,所以整个集成项目导入的 Jar 包汇总如下:

源码展示

1、接下来将内容逐个进行粘贴展示,按照开发是代码的先后顺序进行展示。

User 实体内容如下:

import java.util.Date;

/**
 * Created by Administrator on 2019/2/19 0019.
 * 用户实体
 */
public class User {
    private Integer uId;//用户id
    private String uName;//用户姓名
    private Date bithday;//用户数生日
    private Float price;//用户身价

    public Date getBithday() {
        return bithday;
    }

    public void setBithday(Date bithday) {
        this.bithday = bithday;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    public Integer getuId() {
        return uId;
    }

    public void setuId(Integer uId) {
        this.uId = uId;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    @Override
    public String toString() {
        return "User{" +
                "bithday=" + bithday +
                ", uId=" + uId +
                ", uName='" + uName + '\'' +
                ", price=" + price +
                '}';
    }
}

UserService 接口内容如下:

import com.lct.www.domain.User;

import javax.jws.WebService;

/**
 * Created by Administrator on 2019/2/19 0019.
 * 用户接口
 *
 * @WebService :标识此接口为 webService 接口
 */
@WebService
public interface UserService {

    /**
     * 根据用户id查询用户信息
     *
     * @param uId
     * @return
     */
    public User getUserById(Integer uId);
}

UserServiceImpl 服务接口实现类内容如下:

import com.lct.www.domain.User;
import com.lct.www.service.UserService;
import org.springframework.stereotype.Service;

import javax.jws.WebService;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/19 0019.
 *
 * @WebService :标识此类为 webService 服务类, 注意 endpointInterface 属性不用再写,否则报错
 */
@WebService
@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = Logger.getGlobal();//日志记录器

    @Override
    public User getUserById(Integer uId) {
        logger.log(Level.INFO, "根据用户id查询用户信息,uId=" + uId);
        //用于演示,则不从数据查询,这里直接造一个假数据返回
        User user = new User();
        user.setuId(uId);
        user.setuName(UUID.randomUUID().toString());
        user.setBithday(new Date());
        user.setPrice(new Random().nextInt(10000000) * 1.0F);

        logger.log(Level.INFO, "查出用户信息为:" + user);
        return user;
    }
}

UserController 控制器内容如下:

import com.lct.www.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

/**
 * Created by Administrator on 2019/2/19 0019.
 * 用户控制器
 */
@Controller
public class UserController {
    @Resource
    private UserService userService;

    /**
     * 根据用户id获取用户信息
     * 浏览器访问地址为:http://localhost/web_service_spring_s/getUserById.action
     *
     * @return 直接将内容打印到页面
     */
    @RequestMapping("getUserById.action")
    public void getUserById(HttpServletResponse response) {
        try {
            PrintWriter printWriter = response.getWriter();
            printWriter.write(userService.getUserById(new Random().nextInt(1000)).toString());
            printWriter.flush();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

配置文件

beasn.xml 内容如下(注意xml元素的命名空间在头信息中都必须存在):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	   					   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
						   http://www.springframework.org/schema/context
						   http://www.springframework.org/schema/context/spring-context-4.3.xsd
		              	   http://www.springframework.org/schema/tx
						   http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		              	   http://www.springframework.org/schema/mvc
		              	   http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		              	   http://cxf.apache.org/jaxws
		              	   http://cxf.apache.org/schemas/jaxws.xsd">

    <!-- 包扫描,因为没有操作数据库,所以没有dao层,只有servive层-->
    <context:component-scan base-package="com.lct.www.service.impl"/>

    <!-- 引入 cxf 的一些核心配置,这些文件文件都在导入的 CXF 核心包中已经提供了的 -->
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <!-- 如果是 2.5.9 这样的老版本 cxf,则还有以下两个文件也要写上,3.3版本时已经没有了,所以不要写-->
    <!--<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>-->
    <!--<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>-->

    <!-- webService服务终端配置-->
    <!-- id:标识此服务终端,唯一,自定义名称即可,因为可以写多个 endpoint,千万注意不要和其它bean的名称冲突,特别是service层 -->
    <!-- implementor:服务接口的实现类全路径-->
    <!-- address:将来客户端请求 webService url -->
    <!-- 相当于Endpoint.publish("http://localhost:8080/web_service_spring_s/userService", UserServiceImpl());-->
    <!-- 经实测,address 属性值无法多级,如 /cxf/userService,则只有 /cxf有效,后面的 /userService无效-->
    <jaxws:endpoint id="userServiceWebS" implementor="com.lct.www.service.impl.UserServiceImpl"
                    address="/userService">
    </jaxws:endpoint>
</beans>

springMVC.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
						http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
						http://www.springframework.org/schema/context
          				http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- 对所有的Controller进行包扫描 -->
    <context:component-scan base-package="com.lct.www.controller"/>

    <!-- 内部资源视图解析器 -->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

web.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- 1、配置spring容器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 2、配置springMVC-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
    </servlet>
    <!-- 对所有后缀名为 .action的请求进行过滤处理-->
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

    <!-- 3、配置中文乱码过滤器-->
    <filter>
        <filter-name>SpringEncoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SpringEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 注意 CXF 的 url-pattern 一定要和 SpringMVC 的 url-pattern 分离,否则二者的功能只能使用其一-->
    <!-- 假设 CXF 的 url-pattern 为 "/*" ,则所有的请求都被 CXF 过滤了,包括那些不是 webService 的服务请求,从而导致- -->
    <!-- SpringMVC 应该请求后台控制器的也被当做 webService 请求被处理了-->
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/userService/*</url-pattern>
    </servlet-mapping>
</web-app>

浏览器访问测试

将生成的 xml 内容粘贴如下:

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.service.www.lct.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://service.www.lct.com/" name="UserServiceImplService" targetNamespace="http://impl.service.www.lct.com/">
  <wsdl:import location="http://localhost:8080/web_service_spring_s/userService/userService?wsdl=UserService.wsdl" namespace="http://service.www.lct.com/">
    </wsdl:import>
  <wsdl:binding name="UserServiceImplServiceSoapBinding" type="ns1:UserService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getUserById">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="getUserById">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getUserByIdResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="UserServiceImplService">
    <wsdl:port binding="tns:UserServiceImplServiceSoapBinding" name="UserServiceImplPort">
      <soap:address location="http://localhost:8080/web_service_spring_s/userService/userService"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

客户端

1、服务端完成之后,客户端则简单的多了,可以使用 Java JDK 自带的 wsimport.exe 工具生成代理类,也可以使用 CXF 的 wsdl2java。

2、客户端如果是 Java SE 项目,则调用完全和以前的一样,可以参考《Apache CXF 入门第一个示例》,一是导入代理类,然后生成对象,调用方法即可,连 CXF 的包都可以不用导入。

UserServiceImplService userServiceImplService = new UserServiceImplService();
UserService userService = userServiceImplService.getUserServiceImplPort();
System.out.println(userService.getUserById(1));

3、所以这里重点介绍CXF 和 Spring 集成,因为既然使用了 Spring,显然代理类接口就应该交由 Spring 容器来管理,这样后期便可以直接获取然后。

4、同样是 Jdk8+Tomcat9,Spring4.3.10 集成 Apache CXF 3.3,新建 Java Web 项目,web_service_spring_c,导入的 Jar 包和服务端完全一致,整体结构如下。

5、如上所示,演示的流程是,应用启动后,从浏览器访问 UserControl 中的方法,然后使用 UserService 直接调用 webService 服务端,因为不走自己的数据库,所以很简洁。

wsdl2java 生成代理类

1、无论是 Java JDK 自带的 wsimport.exe 工具还是 CXF 的 wsdl2java都是可以的,这里就使用 wsdl2java。

2、cmd 先进入 CXF 的 bin 目录下,然后使用 wsdl2java 工具,-d:表示代理类生成后存放的地方,-encoding:表示编码,-verbose:表示显示过程信息。生成之后将它们全部导入到项目中,包路径使用默认的路径,也就是和服务端保持一致。

PS E:\Study_Note\apache-cxf-3.3.0\bin> .\wsdl2java -d E:/wmx/webservice/cxf4 -encoding UTF-8 -verbose http://localhost:8080/web_service_spring_s/userService/userService?wsdl

3、再重复一句,导入 jar 包完全和服务端时一致,这里不再贴图。

源码内容

1、将所有源码内容逐个粘贴如下,beans.xml 内容如下,CXF 和 Spring 集成主要就是里面的那一句:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	   					   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
						   http://www.springframework.org/schema/context
						   http://www.springframework.org/schema/context/spring-context-4.3.xsd
		              	   http://www.springframework.org/schema/tx
						   http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
		              	   http://www.springframework.org/schema/mvc
		              	   http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		              	   http://cxf.apache.org/jaxws
		              	   http://cxf.apache.org/schemas/jaxws.xsd">

    <!-- jaxws:client 这里 spring会给它自动进行实例化,为 UserService 接口创建实例,后期自己使用@Resource获取使用即可-->
    <!-- id:bean 的唯一标识,使用接口名首字母小写即可;serviceClass:服务接口的全路径,会交由spring容器管理,实例化-->
    <!-- address:webService 服务端 wsdl 的访问路径,注意这里结尾不用加 "?wsdl",以后可以直接调用 serviceClass 中的方法-->
    <jaxws:client id="userService"
                  serviceClass="com.lct.www.service.UserService"
                  address="http://localhost:8080/web_service_spring_s/userService/userService">
    </jaxws:client>
</beans>

2、这是 SpringMVC 的内容,配置控制器包扫描,以及视图解析器,springMVC.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
						http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
						http://www.springframework.org/schema/context
          				http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- 对所有的Controller进行包扫描 -->
    <context:component-scan base-package="com.lct.www.controller"/>

    <!-- 内部资源视图解析器 -->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

3、web.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- 1、配置spring容器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 2、配置springMVC-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
    </servlet>
    <!-- 对所有后缀名为 .action的请求进行过滤处理-->
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

    <!-- 3、配置中文乱码过滤器-->
    <filter>
        <filter-name>SpringEncoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SpringEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

4、UserController内容如下:

import com.lct.www.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created by Administrator on 2019/2/19 0019.
 * 用户控制器
 */
@Controller
public class UserController {

    //生成的代理类,也是接口和实现类的结构,已经交由了Spring容器管理,所以直接取值即可
    @Resource
    private UserService userService;

    /**
     * 根据用户id获取用户信息
     * 浏览器访问地址为:http://localhost/web_service_spring_s/getUserById.action
     *
     * @return 直接将内容打印到页面
     */
    @RequestMapping("getUserById.action")
    public void getUserById(HttpServletResponse response) {
        try {
            PrintWriter printWriter = response.getWriter();
            printWriter.write(userService.getUserById(1).toString());
            printWriter.flush();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

浏览器访问测试

服务端控制台日志输出如下:

20-Feb-2019 15:49:03.766 信息 [http-nio-8080-exec-10] com.lct.www.service.impl.UserServiceImpl.getUserById 根据用户id查询用户信息,uId=1
20-Feb-2019 15:49:03.767 信息 [http-nio-8080-exec-10] com.lct.www.service.impl.UserServiceImpl.getUserById 查出用户信息为:User{bithday=Wed Feb 20 15:49:03 CST 2019, uId=1, uName='e5473511-98d6-4edc-afb2-b331702f9217', price=9381686.0}

配置拦截器

1、《Apache CXF 拦截器示例》中的使用代码添加的拦截器,这里改为在 beans.xml 文件中进行配置,拦截器的内容是一样的,仅仅是配置不同。

服务端

1、这里只粘贴修改过的的内容,WsUserInterceptor 内容如下:

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 javax.xml.namespace.QName;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/18 0018.
 * 自定义拦截器——————用于验证客户端的账号密码
 */
public class WsUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    private static final Logger logger = Logger.getGlobal();//日志记录器

    /**
     * 构造器中必须使用 super 设置拦截器发生的时刻/阶段
     * org.apache.cxf.phase.Phase 中提供了大量的常量表示拦截器发生的时刻
     */
    public WsUserInterceptor() {
        super(Phase.PRE_PROTOCOL);//协议前进行拦截
    }

    /**
     * 客户端传来的 soap 消息先进入拦截器这里进行处理,客户端的账目与密码消息放在 soap 的消息头<head></head>中,类似如下:
     * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><userInfo><name>admin</name><password>123456</password></userInfo></soap:Header><soap:Body><ns2:getStudentById xmlns:ns2="http://web_service.lct.com/"><sId>11</sId></ns2:getStudentById></soap:Body></soap:Envelope>
     * 现在只需要解析其中的 <head></head>标签,如果解析验证成功,则放行,否则这里直接抛出异常,服务端不会再往后运行,客户端也会跟着抛出异常,得不到正确结果
     *
     * @param message
     * @throws Fault
     */
    @Override
    public void handleMessage(SoapMessage message) throws Fault {

        /**org.apache.cxf.headers.Header
         * QName :xml 限定名称,客户端设置头信息时,必须与服务器保持一致,否则这里返回的 header 为null,则永远通不过的
         */
        Header header = message.getHeader(new QName("userInfo"));
        if (header != null) {
            Element rootEle = (Element) header.getObject();//获取根元素,即 <userInfo>标签
            String name = rootEle.getElementsByTagName("name").item(0).getTextContent();//获取元素值
            String password = rootEle.getElementsByTagName("password").item(0).getTextContent();//获取元素值
            /**为了演示简单,直接写死了,实际中建议放在配置文件中提供配置*/
            if ("admin".equals(name) && "123456".equals(password)) {
                logger.info("webService 服务端自定义拦截器验证通过....");
                return;//放行
            }
        }
        /**
         * 验证失败,客户端没有提供账号密码、或者提供账号密码错误
         */
        logger.info("webService 服务端自定义拦截器验证失败....");
        throw new Fault(new RuntimeException("请提供正确的用户名和密码!格式:<Header><userInfo><name>xxx</name><password>xxx</password></userInfo></Header>"));
    }
}

2、beans.xml 中进行配置拦截器,只粘贴修改的部分内容:

    <!-- 引入 cxf 的一些核心配置,这些文件文件都在导入的 CXF 核心包中已经提供了的 -->
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <!-- 如果是 2.5.9 这样的老版本 cxf,则还有以下两个文件也要写上,3.3版本时已经没有了,所以不要写-->
    <!--<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>-->
    <!--<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>-->

    <!-- webService服务终端配置-->
    <!-- id:标识此服务终端,唯一,自定义名称即可,因为可以写多个 endpoint,千万注意不要和其它bean的名称冲突,特别是service层 -->
    <!-- implementor:服务接口的实现类全路径-->
    <!-- address:将来客户端请求 webService url -->
    <!-- 相当于Endpoint.publish("http://localhost:8080/web_service_spring_s/userService", UserServiceImpl());-->
    <!-- 经实测,address 属性值无法多级,如 /cxf/userService,则只有 /cxf有效,后面的 /userService无效-->
    <jaxws:endpoint id="userServiceWebS" implementor="com.lct.www.service.impl.UserServiceImpl"
                    address="/userService">

        <!-- 配置 CXF 入拦截器-->
        <jaxws:inInterceptors>
            <!-- LoggingInInterceptor:这是 CXF 默认提供的日志入拦截器,在开发调试阶段有用,部署阶段可以删除-->
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
            <!-- WsUserInterceptor:自定义的入拦截器,用于账号密码验证-->
            <bean class="com.lct.www.interceptors.WsUserInterceptor"/>
        </jaxws:inInterceptors>
    </jaxws:endpoint>

3、先写拦截器,再配置拦截器,其余内容不变。

客户端

1、WsUserInterceptor 内容如下:

import com.sun.org.apache.xml.internal.utils.DOMHelper;
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.Document;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;
import java.util.List;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/18 0018.
 * 自定义 webService 拦截器——————用于提供请求的账号与密码
 */
public class WsUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    private static final Logger logger = Logger.getGlobal();//日志记录器
    /**
     * webService 需要传递的账号与密码
     */
    private String name;
    private String password;

    /**
     * 使用构造器传入参数,构造器中必须使用 super 设置拦截器发生的时刻/阶段
     * org.apache.cxf.phase.Phase 中提供了大量的常量表示拦截器发生的时刻
     *
     * @param name
     * @param password
     */
    public WsUserInterceptor(String name, String password) {
        super(Phase.PRE_PROTOCOL);//协议前进行拦截
        this.name = name;
        this.password = password;
    }

    /**
     * 默认情况下,客户端给服务端请求的 soap 消息如下:
     * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getStudentByIdResponse xmlns:ns2="http://web_service.lct.com/"><getStudentById><birthday>2019-02-18T16:19:12.102+08:00</birthday><punishStatus>5</punishStatus><sID>11</sID><sName>0a90959c-22ee-497b-b60d-0550b1f2adf7</sName></getStudentById></ns2:getStudentByIdResponse></soap:Body></soap:Envelope>
     * 其中的 <body></body>部分为请求的主体,现在为 Envelope 设置头信息 <head></head>,将账户密码信息放在 <head></head>中,<head>与 body 同级
     * <head></head>中的内容设置为账户与密码元素,如:
     * <head><userInfo><name>admin</name><password>123456</password></userInfo></head>,其中的 userInfo、name、password 等元素名称必须和服务端约定的一致,
     * 因为服务端还需要根据这些名称解析出元素的值
     *
     * @param message :在客户端请求服务端前,先进入此方法,然后将账户密码信息添加到 soap 消息头中
     * @throws Fault
     */
    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        /**
         * com.sun.org.apache.xml.internal.utils.DOMHelper
         * org.w3c.dom.Document
         * org.w3c.dom.Element
         */
        Document document = DOMHelper.createDocument();//创建 w3c 的文档对象
        Element userInfoEle = document.createElement("userInfo");//创建标签元素
        Element nameEle = document.createElement("name");//创建标签元素
        Element passwordEle = document.createElement("password");//创建标签元素

        nameEle.setTextContent(this.name);//设置标签元素内容
        passwordEle.setTextContent(this.password);//设置标签元素内容
        userInfoEle.appendChild(nameEle);//添加子元素
        userInfoEle.appendChild(passwordEle);//添加子元素

        /**
         * org.apache.cxf.headers.Header
         * 最后将创建好的 soap 头信息添加到 SoapMessage 中
         */
        List<Header> headerList = message.getHeaders();
        /**QName构造器中的值与后面的 userInfoEle 元素的标签名保持一致*/
        headerList.add(new Header(new QName("userInfo"), userInfoEle));
        logger.info("客户端 webServic 自定义出拦截器执行完毕....");
    }
}

2、beans.xml 文件中配置拦截器,与服务端配置完全一致,仅将变动的内容粘贴如下:

    <!-- jaxws:client 这里 spring会给它自动进行实例化,为 UserService 接口创建实例,后期自己使用@Resource获取使用即可-->
    <!-- id:bean 的唯一标识,使用接口名首字母小写即可;serviceClass:服务接口的全路径,会交由spring容器管理,实例化-->
    <!-- address:webService 服务端 wsdl 的访问路径,注意这里结尾不用加 "?wsdl",以后可以直接调用 serviceClass 中的方法-->
    <jaxws:client id="userService"
                  serviceClass="com.lct.www.service.UserService"
                  address="http://localhost:8080/web_service_spring_s/userService/userService">

        <!-- 出拦截器配置-->
        <jaxws:outInterceptors>
            <!--LoggingOutInterceptor 是CXF自身提供的日志出拦截器,直接使用即可 -->
            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
            <!--WsUserInterceptor:自定义的拦截器,用于提供验证账号与密码,通过构造器赋值 -->
            <bean class="com.lct.www.interceptors.WsUserInterceptor">
                <constructor-arg name="name" value="admin"/>
                <constructor-arg name="password" value="123456"/>
            </bean>
        </jaxws:outInterceptors>
    </jaxws:client>

3、其余内容不变。

访问测试

1、服务端使用自定义拦截器要求客户端调用时提供账号密码,账号:admin,密码:123456。

2、这里测试时先使用正确的账号密码进行调用,然后使用错误的账号 root 进行调用。

账号:admin,密码:123456 调用时如下:

1)此时客户端控制打印日志如下,可以看到<head>头信息中正确的的账号与密码:

---------------------------
ID: 1
Address: http://localhost:8080/web_service_spring_s/userService/userService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><userInfo><name>admin</name><password>123456</password></userInfo></soap:Header><soap:Body><ns2:getUserById xmlns:ns2="http://service.www.lct.com/"><arg0>1</arg0></ns2:getUserById></soap:Body></soap:Envelope>
--------------------------------------

2)此时服务端控制端输出信息如下:

----------------------------
ID: 1
Address: http://localhost:8080/web_service_spring_s/userService/userService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[291], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=[""], user-agent=[Apache-CXF/3.3.0]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><userInfo><name>admin</name><password>123456</password></userInfo></soap:Header><soap:Body><ns2:getUserById xmlns:ns2="http://service.www.lct.com/"><arg0>1</arg0></ns2:getUserById></soap:Body></soap:Envelope>
--------------------------------------
20-Feb-2019 16:26:00.535 信息 [http-nio-8080-exec-7] com.lct.www.interceptors.WsUserInterceptor.handleMessage webService 服务端自定义拦截器验证通过....
20-Feb-2019 16:26:00.629 信息 [http-nio-8080-exec-7] com.lct.www.service.impl.UserServiceImpl.getUserById 根据用户id查询用户信息,uId=1
20-Feb-2019 16:26:00.629 信息 [http-nio-8080-exec-7] com.lct.www.service.impl.UserServiceImpl.getUserById 查出用户信息为:User{bithday=Wed Feb 20 16:26:00 CST 2019, uId=1, uName='e26e2162-09a3-4de1-b1b0-1eb66ff4128e', price=7310311.0}

账号:root,密码:123456 调用时如下:

服务端控制台日志信息如下:

----------------------------
ID: 2
Address: http://localhost:8080/web_service_spring_s/userService/userService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[290], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=[""], user-agent=[Apache-CXF/3.3.0]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><userInfo><name>root</name><password>123456</password></userInfo></soap:Header><soap:Body><ns2:getUserById xmlns:ns2="http://service.www.lct.com/"><arg0>1</arg0></ns2:getUserById></soap:Body></soap:Envelope>
--------------------------------------
20-Feb-2019 16:29:25.225 信息 [http-nio-8080-exec-2] com.lct.www.interceptors.WsUserInterceptor.handleMessage webService 服务端自定义拦截器验证失败....
20-Feb-2019 16:29:25.229 警告 [http-nio-8080-exec-2] org.apache.cxf.phase.PhaseInterceptorChain.doDefaultLogging Interceptor for {http://impl.service.www.lct.com/}UserServiceImplService has thrown exception, unwinding now
 org.apache.cxf.interceptor.Fault: 请提供正确的用户名和密码!格式:<Header><userInfo><name>xxx</name><password>xxx</password></userInfo></Header>
	at com.lct.www.interceptors.WsUserInterceptor.handleMessage(WsUserInterceptor.java:57)

测试成功。

Github 服务端源码地址:https://github.com/wangmaoxiong/web_service_spring_s

Github 客户端源码地址:https://github.com/wangmaoxiong/web_service_spring_c

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/87713876