SpringBoot整合CXF发布WebService接口,及动态调用(用户名和密码验证)

本文介绍了如何在Spring Boot中整合CXF发布WebService服务,包括添加依赖、定义接口、实现权限控制的拦截器、配置服务发布,并展示了客户端如何调用接口,包括设置拦截器和测试调用。动态调用方式无需依赖service类,简化了调用流程。

打开这篇文章的伙伴们说明已经对WebService有所了解,我这里就不啰嗦理论了,直接上代码。

一、WebService 服务端

1、在pom里加入 CXF 框架所需的依赖

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.5.1</version>
</dependency>

2、 创建你服务端接口

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/*
*  @WebService 标记服务端点接口
*      name:此属性的值包含XML Web Service的名称
*      targetNamespace:指定你想要的名称空间,使用接口实现类的包名的反缀
*
*  如果不写这targetNamespace参数,你可以访问的wsdl,但是客户端动态调用invoke的时候,会找不到接口
*
*  这个接口的实现类上可以不添加@WebService注解
* */
@WebService(name = "studentService",targetNamespace = "http://service.testwebservice.demo.com")
public interface StudentService {

    /*
    *  @WebMethod 接口的方法名
    *       operationName:方法名称
    *       exclude:用于阻止将某一继承方法公开为web服务,默认为false
    *  @WebParam 接口的参数
    *       如果你不加 @WebParam(name = "param"),那么在你的wsdl 中的默认参数名就是 arg0
    * */
    @WebMethod
    String getStudent(@WebParam(name = "param") String param);
}

3、实现接口

import org.springframework.stereotype.Component;


@Component
public class StudentServiceImpl implements StudentService {

    public String getStudent(String param) {
        // 打印客户端的参数
        System.err.println(param);
        
        return "查学历";
    }
}

4、 自定义拦截器实现权限控制

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;

import java.util.List;

public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    //在调用之前拦截
    public AuthInterceptor() {
        super(Phase.PRE_INVOKE);
    }

    /**
     * 自定义拦截器需要实现handleMessage方法,该方法抛出Fault异常,可以自定义异常集成自Fault,
     * 也可以new Fault(new Throwable())
     */
    public void handleMessage(SoapMessage soap) throws Fault {
        System.out.println("开始验证用户信息");
        List<Header> headers = soap.getHeaders();

        //检查headers是否存在
        if(headers == null | headers.size()<1){
            throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息"));
        }

        Header header = headers.get(0);

        Element el = (Element)header.getObject();
        NodeList users = el.getElementsByTagName("username");
        NodeList passwords = el.getElementsByTagName("password");

        //检查是否有用户名和密码元素
        if(users.getLength()<1){
            throw new Fault(new IllegalArgumentException("找不到用户信息"));
        }
        String username = users.item(0).getTextContent().trim();

        if(passwords.getLength()<1){
            throw new Fault(new IllegalArgumentException("找不到密码信息"));
        }
        String password = passwords.item(0).getTextContent();

        //检查用户名和密码是否正确
        if(!"admin".equals(username) || !"admin".equals(password)){
            throw new Fault(new IllegalArgumentException("用户名或密码不正确"));
        }else{
            System.out.println("用户名密码正确允许访问");
        }
    }
}

5、配置类发布服务

import com.demo.testwebservice.service.impl.StudentServiceImpl;
import com.demo.testwebservice.utils.AuthInterceptor;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class WebServiceConfig {

    /*
    *  由于 SpringBoot 默认是以 jar 包的方式启动嵌入式的 Servlet 容器,启动 SpringBoot 的 web 应用,没有 web.xml 文件
    *  所以想用使用 Servlet 功能,就必须要借用 Spring Boot 提供的 ServletRegistrationBean 接口
    *
    *  配置webservice的前缀路径
    * */
    @Bean
    public ServletRegistrationBean registrationBean(){
        ServletRegistrationBean rsb = new ServletRegistrationBean(new CXFServlet(),"/student_webservice/*");
        return rsb;
    }

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

    @Bean
    public StudentServiceImpl studentService(){
        return new StudentServiceImpl();
    }

    // 配置路径后缀,发布服务
    @Bean
    public Endpoint endpoint(){
        EndpointImpl endpointImpl = new EndpointImpl(springBus(),studentService());
        //通过getInInterceptors方法,向WebService服务添加拦截器。
        endpointImpl.getInInterceptors().add(new AuthInterceptor());
        // 配置路径后缀
        endpointImpl.publish("/student");
        return endpointImpl;
    }
}

5、启动Spring boot ,浏览器输入:http://localhost:8888/student_webservice/student?wsdl

  •  访问到wsdl,说明你的服务发布成功,下一步客户端调用接口

 二、WebService 客户端

1、引用 CXF 依赖,同服务端的依赖外再加如下依赖

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-bindings-soap</artifactId>
    <version>3.5.1</version>
    <scope>compile</scope>
</dependency>

2、客户端拦截器

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;

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

public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    private String username;
    private String password;
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    /**
     * 创建一个新的实例 ClientLoginInterceptor.
     *
     * @param username
     * @param password
     */
    public ClientLoginInterceptor(String username, String password) {
        super(Phase.PREPARE_SEND);
        this.username = username;
        this.password = password;
    }


    public void handleMessage(SoapMessage soap) throws Fault {
        
        List<Header> headers = soap.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));
    }

}

3、测试类编写接口的调用

import com.demo.client.utils.ClientLoginInterceptor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.xml.namespace.QName;
 

   @Test
    void contextLoads() throws Exception {

        //方式2

        //JaxWsDynamicClientFactory 只要指定服务器端wsdl文件的位置,然后指定要调用的方法和方法的参数即可,不关心服务端的实现方式
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient("http://localhost:8888/student_webservice/student?wsdl");
        // 设置用户名密码
        client.getOutInterceptors().add(new ClientLoginInterceptor("admin", "admin"));
        Object[] objects = new Object[0];
        try {
            QName opName = new QName("http://service.testwebservice.demo.com", "getStudent");   //参数1 是targetNamespace的空间名,参数2 是方法名
            objects = client.invoke(opName,"我是传的参数");
            System.out.println("返回数据:" + objects[0]);
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }

    }

3、服务端的返回值

 4、客户端的参数

总结: 

  • 动态调用完全不依赖service类,服务器端只要提供接口名和路径就可以方便的调用
  • 静态调用需要依赖service类,因为客户端调用cxf webservice接口的过程中需要服务器端提供service,很不方便,如果同一个项目中则没有区别

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值