springboot(十)集成CXF发布webservice以及客户端调用

x33g5p2x  于2021-12-17 转载在 其他  
字(6.8k)|赞(0)|评价(0)|浏览(360)

概述:

    Web service是一个pin独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。

    更多理论知识请自行百度一下。 

sprinboot集成cxf:

新建项目:springboot-webservice,pom.xml:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--cxf依赖-->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.3.1</version>
        </dependency>
    </dependencies>

新建接口DemoService

@javax.jws.WebService(name="DemoService",targetNamespace = "http://service.springbootwebservice.example.com")
public interface DemoService {
    @WebMethod
    String say(@WebParam(name = "name") String name);
}

   @WebService :该注解用于对接口,类进行注解,表示要发布的web服务

              name :暴露服务名称

              targetNamespace : 命名空间,一般为接口的包名的倒序

   @WebMethod:该注解用于用@WebService注解的类或接口的方法上,表示要发布的方法:

   @WebParam:该注解表示方法的参数

实现类DemoServiceImpl.java:

@javax.jws.WebService( serviceName = "DemoService"
        ,targetNamespace = "http://service.springbootwebservice.example.com"
        ,endpointInterface = "com.example.springbootwebservice.service.DemoService")
@Component
public class DemoServiceImpl implements DemoService {
    @Override
    public String say(String name) {
        return "我是:" + name;
    }

}

**     @WebService** :该注解用于对接口,类进行注解,表示要发布的web服务

               serviceName :服务名 与接口中指定的name一致

               targetNamespace:接口包名倒叙,并且和接口定义保持一致

               endpointInterface:接口地址

  编写拦截器AuthInterceptor.java:用于访问用户验证       

import java.util.List;
import javax.xml.soap.SOAPException;
import org.apache.cxf.binding.soap.SoapHeader;
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.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    //默认登陆用户
    private String USERNAME="admin";
    //默认登陆用户密码
    private String PASSWORD="123456";

    public AuthInterceptor() {
        // 定义在什么阶段进行拦截
        super(Phase.PRE_PROTOCOL);
    }

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        String username = null;
        String password = null;
        List<Header> headers = soapMessage.getHeaders();
        if(headers == null) {
            throw new Fault(new IllegalArgumentException("headers未取到,无法验证用户信息"));
        }
        // 获取客户端传递的用户名和密码
        for (Header header : headers) {
            SoapHeader soapHeader = (SoapHeader) header;
            Element e = (Element) soapHeader.getObject();
            NodeList usernameNode = e.getElementsByTagName("username");
            NodeList passwordNode = e.getElementsByTagName("password");
            username = usernameNode.item(0).getTextContent();
            password = passwordNode.item(0).getTextContent();
            if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                throw new Fault(new IllegalArgumentException("用户信息为空!"));
            }
        }
        // 校验客户端用户名密码是否和服务端一致
        if(!(username.equals(USERNAME) && password.equals(PASSWORD))) {
            throw new Fault(new SOAPException("用户信息认证失败!"));
        }
    }

}

配置类CxfConfig.java:

@Configuration
public class CxfConfig {
    /**
     * 此方法作用是改变项目中服务名的前缀名
     * wsdl的默认访问地址为http://127.0.0.1:8080/services/*  也就是/service/*
     * 我们可以自己配置:wsdl访问地址为:http://127.0.0.1:8080/webservice/*
     * 这里我们自定义服务名为/webservice/*
     * 当然还可以直接在application.properties文件中加上cxf.path=/webservice
     * @return
     */
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        ServletRegistrationBean bean = new ServletRegistrationBean(
                new CXFServlet(), "/webservice/*");
        bean.setLoadOnStartup(0);
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }

    //把实现类交给spring管理
    @Bean
    public DemoService webService() {
        return new DemoServiceImpl();
    }

    //终端路径
    @Bean
    public Endpoint endpoint() {
        //绑定要发布的服务
        EndpointImpl endpoint = new EndpointImpl(springBus(), webService());
        // 服务端添加自定义拦截器 不需要验证可以省略此方法
        endpoint.getInInterceptors().add(new AuthInterceptor());
        //绑定要发布的服务名称
        endpoint.publish("/ws");
        return endpoint;
    }
}

编写拦截器ClientInterceptor.java:用于在请求头添加用户信息用于验证

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 ClientInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    //登陆用户
    private String username;
    //登陆密码
    private String password;

    public ClientInterceptor(String username, String password) {
        // 发送请求之前进行拦截
        super(Phase.PREPARE_SEND);
        this.username = username;
        this.password = password;
    }

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        List<Header> headers = soapMessage.getHeaders();
        Document doc = DOMUtils.createDocument();
        Element auth = doc.createElement("authrity");
        Element username = doc.createElement("username");
        Element password = doc.createElement("password");
        username.setTextContent(this.username);
        password.setTextContent(this.password);
        auth.appendChild(username);
        auth.appendChild(password);
        headers.add(0, new Header(new QName("tiamaes"), auth));
    }

}

编写客户端CxfClient.java

public class Cxfclient {
    // 服务端接口地址
    private static String address = "http://localhost:8080/webservice/ws?wsdl";

    public static void main(String[] args) {
        method1();
        method2();
    }
    /**
     * 方式1.代理类工厂方式,需要拿到对方的接口
     */
    public static void method1() {
        try {
            // 代理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置代理地址
            jaxWsProxyFactoryBean.setAddress(address);
            // 添加用户信息验证
            jaxWsProxyFactoryBean.getOutInterceptors().add(new ClientInterceptor("admin", "123456"));
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(DemoService.class);
            // 创建一个代理接口实现
            DemoService ws = (DemoService) jaxWsProxyFactoryBean.create();
            // 调用代理接口的方法调用并返回结果
           String result = ws.say("lisi");
            System.out.println("代理类工厂方式返回结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 动态调用方式
     */
    public static void method2() {
        // 创建动态客户端
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(address);
        // 添加用户信息验证
        client.getOutInterceptors().add(new ClientInterceptor("admin", "123456"));
        Object[] objects = new Object[0];
        try {
            // invoke("方法名",参数1,参数2,参数3....);
            objects = client.invoke("say", "zhangsan");
            System.out.println("动态调用方式结果:" + objects[0]);
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }
}

    注:如果不想要登陆验证请注释以下代码:

           1.CxfConfig.java类中endpoint()方法中:endpoint.getInInterceptors().add(new AuthInterceptor());

           2.Cxfclient.java类中动态调用方法中:client.getOutInterceptors().add(new ClientInterceptor("admin", "123456"));

           3.Cxfclient.java类中工厂调用方法中:jaxWsProxyFactoryBean.getOutInterceptors().add(new ClientInterceptor("admin", "123456"));

          其实注释1就可以啦,2、3不注释也没啥影响的。个人习惯!

源码地址https://gitee.com/xu0123/springboot2

相关文章