SpringBoot整合WebService

x33g5p2x  于2021-12-06 转载在 Spring  
字(9.1k)|赞(0)|评价(0)|浏览(364)

WebService是一种传统的SOA技术架构,它不依赖于任何的编程语言,也不依赖于任何的技术平台,可以直接基于HTTP协议实现网络应用间的数据交互。

WebService主要用于异构平台之间的整合与调用,例如请求者使用的是Java语言开发,而提供者是Golang语言开发。使用XML进行接口的描述(SOAP协议)。

SpringBoot搭建WebService程序

在springboot-webservice项目中新建3个模块,webservice-server、webservice-client、webservice-common。

webservice-common项目引入项目依赖,webservice-server和webservice-client项目引入webservice-common项目。

  1. <dependency>
  2. <groupId>com.sun.xml.ws</groupId>
  3. <artifactId>jaxws-ri</artifactId>
  4. <version>2.3.5</version>
  5. <type>pom</type>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.hibernate.validator</groupId>
  9. <artifactId>hibernate-validator</artifactId>
  10. <version>7.0.1.Final</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.apache.cxf</groupId>
  14. <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
  15. <version>3.4.3</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.apache.cxf</groupId>
  19. <artifactId>cxf-rt-transports-http</artifactId>
  20. <version>3.4.3</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter-web</artifactId>
  25. <version>2.6.0</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-web-services</artifactId>
  30. <version>2.6.0</version>
  31. </dependency>

一、定义规范接口

WebService服务端以远程接口为主,在Java实现的WebService技术中,使用CXF开发框架可以直接将接口发布成WebService。

webservice-common模块在com.it.service包下新建MessageService接口。

  1. package com.it.service;
  2. import javax.jws.WebMethod;
  3. import javax.jws.WebParam;
  4. import javax.jws.WebService;
  5. @WebService(name = "MessageService", targetNamespace = "http://service.it.com/")
  6. public interface MessageService {
  7. @WebMethod // webservice方法标注
  8. String echo(@WebParam String message);
  9. }

二、搭建WebService服务端

webservice-server模块定义MessageService的实现子类。

  1. package com.it.service.impl;
  2. import com.it.service.MessageService;
  3. import org.springframework.stereotype.Service;
  4. import javax.jws.WebService;
  5. @WebService(serviceName = "MessageService",
  6. targetNamespace = "http://service.it.com/", // 接口命名空间
  7. endpointInterface = "com.it.service.MessageService") // 接口名称
  8. @Service // 注册到Spring容器
  9. public class MessageServiceImpl implements MessageService {
  10. @Override
  11. public String echo(String message) {
  12. return "[echo]: " + message;
  13. }
  14. }

基于拦截器实现安全配置。

  1. package com.it.interceptor;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.cxf.binding.soap.SoapMessage;
  4. import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
  5. import org.apache.cxf.interceptor.Fault;
  6. import org.apache.cxf.phase.AbstractPhaseInterceptor;
  7. import org.apache.cxf.phase.Phase;
  8. import org.springframework.stereotype.Component;
  9. import org.w3c.dom.NodeList;
  10. import javax.xml.soap.SOAPException;
  11. import javax.xml.soap.SOAPHeader;
  12. import javax.xml.soap.SOAPMessage;
  13. import java.util.Objects;
  14. @Slf4j
  15. @Component
  16. public class WebServiceAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
  17. private static final String USER_NAME = "admin"; // 用户名
  18. private static final String USER_PASSWORD = "123456"; //密码
  19. private final SAAJInInterceptor interceptor = new SAAJInInterceptor(); // 创建拦截器
  20. public WebServiceAuthInterceptor() {
  21. super(Phase.PRE_PROTOCOL);
  22. super.getAfter().add(SAAJInInterceptor.class.getName()); // 添加拦截器
  23. }
  24. @Override
  25. public void handleMessage(SoapMessage message) throws Fault {
  26. // 获取指定信息
  27. SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
  28. if (soapMessage == null) { // 没有消息
  29. this.interceptor.handleMessage(message); // 走默认流程
  30. soapMessage = message.getContent(SOAPMessage.class); // 再次获取消息
  31. }
  32. SOAPHeader header = null; // SOAP头信息
  33. try {
  34. header = soapMessage.getSOAPHeader();
  35. } catch (SOAPException e) {
  36. e.printStackTrace();
  37. }
  38. if (header == null) { // 没有头信息
  39. throw new Fault(new IllegalAccessException("找不到header信息"));
  40. }
  41. // 解析XML文档(SOAP是XML结构的)
  42. NodeList usernameList = header.getElementsByTagName("username");
  43. NodeList passwordList = header.getElementsByTagName("password");
  44. if (usernameList.getLength() < 1) {
  45. throw new Fault(new IllegalAccessException("找不到header信息"));
  46. }
  47. if (passwordList.getLength() < 1) {
  48. throw new Fault(new IllegalAccessException("找不到header信息"));
  49. }
  50. String username = usernameList.item(0).getTextContent().trim(); // 获取用户名
  51. String password = passwordList.item(0).getTextContent().trim(); // 获取密码
  52. if (Objects.equals(USER_NAME, username) && Objects.equals(USER_PASSWORD, password)) {
  53. log.info("用户访问成功");
  54. } else {
  55. SOAPException soapException = new SOAPException("用户认证失败");
  56. log.error("用户认证失败");
  57. throw new Fault(soapException);
  58. }
  59. }
  60. }

由于当前的webservice是基于CXF开发的,所以需要定义CXF配置类。

  1. package com.it.config;
  2. import com.it.interceptor.WebServiceAuthInterceptor;
  3. import com.it.service.MessageService;
  4. import org.apache.cxf.Bus;
  5. import org.apache.cxf.jaxws.EndpointImpl;
  6. import org.apache.cxf.transport.servlet.CXFServlet;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import javax.xml.ws.Endpoint;
  12. @Configuration
  13. public class CXFConfig {
  14. private final Bus bus;
  15. private final MessageService messageService;
  16. private final WebServiceAuthInterceptor webServiceAuthInterceptor;
  17. @Autowired
  18. public CXFConfig(Bus bus, MessageService messageService, WebServiceAuthInterceptor webServiceAuthInterceptor) {
  19. this.bus = bus;
  20. this.messageService = messageService;
  21. this.webServiceAuthInterceptor = webServiceAuthInterceptor;
  22. }
  23. public ServletRegistrationBean getServletRegistrationBean() {
  24. return new ServletRegistrationBean(new CXFServlet(), "/services/*"); // 设置webservice访问父路径
  25. }
  26. @Bean
  27. public Endpoint getMessageEndpoint() {
  28. EndpointImpl endpoint = new EndpointImpl(bus, messageService);
  29. endpoint.publish("/MessageService");
  30. endpoint.getInInterceptors().add(webServiceAuthInterceptor); // 添加拦截器
  31. return endpoint;
  32. }
  33. }

新建SpringBoot启动类,启动程序。

  1. package com.it;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class StartWebServiceServer {
  6. public static void main(String[] args) {
  7. SpringApplication.run(StartWebServiceServer.class, args);
  8. }
  9. }

浏览器访问:http://localhost:8080/services 发现webservice发布成功。

WSDL文档也正常出现。

三、搭建WebService客户端

CXF组件下的WebService调用服务使用如下流程:

webservice-client模块创建客户端登录拦截器,设置认证信息。

  1. package com.it.interceptor;
  2. import org.apache.cxf.binding.soap.SoapMessage;
  3. import org.apache.cxf.headers.Header;
  4. import org.apache.cxf.helpers.DOMUtils;
  5. import org.apache.cxf.interceptor.Fault;
  6. import org.apache.cxf.phase.AbstractPhaseInterceptor;
  7. import org.apache.cxf.phase.Phase;
  8. import org.w3c.dom.Document;
  9. import org.w3c.dom.Element;
  10. import javax.xml.namespace.QName;
  11. import java.util.List;
  12. public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
  13. private final String username;
  14. private final String password;
  15. public ClientLoginInterceptor(String username, String password) {
  16. super(Phase.PRE_PROTOCOL);
  17. this.username = username;
  18. this.password = password;
  19. }
  20. @Override
  21. public void handleMessage(SoapMessage message) throws Fault {
  22. List<Header> headers = message.getHeaders(); // 获取全部头信息
  23. Document document = DOMUtils.createDocument(); // 创建xml文档
  24. Element authElement = document.createElement("authority"); // 认证数据节点
  25. Element usernameElement = document.createElement("username");
  26. Element passwordElement = document.createElement("password");
  27. usernameElement.setTextContent(username);
  28. passwordElement.setTextContent(password);
  29. authElement.appendChild(usernameElement);
  30. authElement.appendChild(passwordElement);
  31. headers.add(0, new Header(new QName("authority"), authElement));
  32. }
  33. }

CXF有两种调用方式,代理调用和动态程序调用。使用代理调用:

  1. package com.it.client;
  2. import com.it.interceptor.ClientLoginInterceptor;
  3. import com.it.service.MessageService;
  4. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
  5. public class CXFProxyClient {
  6. private static final String ADDRESS = "http://localhost:8080/services/MessageService?wsdl"; // WebService服务地址
  7. public static void main(String[] args) {
  8. JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
  9. jaxWsProxyFactoryBean.setAddress(ADDRESS);
  10. jaxWsProxyFactoryBean.setServiceClass(MessageService.class);
  11. jaxWsProxyFactoryBean.getOutInterceptors().add(
  12. new ClientLoginInterceptor("admin","123456") // 设置用户名,密码
  13. );
  14. MessageService messageService = (MessageService)jaxWsProxyFactoryBean.create();
  15. String echo = messageService.echo("[webservice proxy invoke]");
  16. System.out.println("echo = " + echo);
  17. }
  18. }

执行程序,接口调用成功。

动态调用:

  1. package com.it.client;
  2. import com.it.interceptor.ClientLoginInterceptor;
  3. import org.apache.cxf.endpoint.Client;
  4. import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
  5. public class CXFDynamicClient {
  6. private static final String ADDRESS = "http://localhost:8080/services/MessageService?wsdl"; // WebService服务地址
  7. public static void main(String[] args) throws Exception {
  8. JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
  9. Client client = dcf.createClient(ADDRESS);
  10. client.getOutInterceptors().add(new ClientLoginInterceptor("admin","123456"));
  11. String message = "dynamic";
  12. Object[] result = client.invoke("echo", message);
  13. System.out.println(result);
  14. }
  15. }

相关文章