SpringBoot整合传统WebService

SpringBoot整合传统WebService

什么是传统WebService

传统的WebService是指使用SOAP协议(Simple Object Access Protocol)来进行通信和数据交换的Web服务。

  • 使用SOAP协议来封装和传输数据,这些数据通常是以XML格式表示

    SOAP(Simple Object Access Protocol)是一种基于XML的协议,用于在网络上进行信息交换。它定义了一种标准的消息格式和通信模式,使得不同的应用程序能够通过网络进行通信和数据交换。

SOAP协议使用XML格式来描述消息和数据,通过HTTP协议进行传输。它定义了一种通用的消息结构,包括消息头和消息体,可以在消息中传递不同类型的数据。

SOAP协议还定义了一套规范的操作和错误处理机制,使得应用程序能够进行远程调用和错误处理。

SOAP协议具有以下特点:

  1. 独立于平台和语言:SOAP协议是独立于平台和语言的,可以在不同的操作系统和编程语言之间进行通信和数据交换。
  2. 灵活的消息格式:SOAP协议使用XML格式来描述消息和数据,可以传递复杂的数据结构和对象。
  3. 强大的功能和扩展性:SOAP协议提供了一套强大的功能和扩展机制,可以支持复杂的消息传输和处理,适用于企业级应用和数据交换。
  4. 可靠和安全:SOAP协议提供了可靠的消息传输和安全机制,可以确保通信和数据的安全性。
    SOAP协议广泛应用于Web服务和分布式系统中,通过定义接口和消息格式,实现不同系统之间的集成和交互。
  • 使用WSDL(Web Services Description Language)来描述服务的接口和操作

    WSDL(Web Services Description Language)是一种用于描述Web服务接口的XML格式标记语言。它定义了Web服务的接口、操作、消息和网络地址等信息,可以帮助客户端生成代码和调用服务。

WSDL文件通常由服务提供者创建,并在服务发布时提供给客户端。它包含了以下主要部分:

  1. 服务接口(PortType):描述了服务所支持的操作和消息,以及它们之间的关系。每个操作定义了输入和输出消息的结构。
  2. 消息(Message):定义了操作的输入和输出参数的数据类型和结构。消息可以包含多个部分,每个部分可以是简单类型(如字符串)或复杂类型(如自定义对象)。
  3. 绑定(Binding):将服务接口与具体的协议和消息格式绑定在一起。它定义了服务的网络地址、协议(如SOAP)和消息的编码方式。
  4. 服务(Service):描述了服务的名称和网络地址。一个WSDL文件可以包含多个服务,每个服务可以有多个端口。

WSDL文件可以通过HTTP或其他适当的方式公开,使得客户端可以通过解析WSDL文件来了解服务的接口和操作。客户端可以使用WSDL文件生成代码,以便调用服务的方法和传递参数。

WSDL是一种标准化的描述语言,它提供了一种统一的方式来描述Web服务的接口和操作。它使得不同的应用程序可以通过解析WSDL文件来理解和调用服务,实现了跨平台和松耦合的应用集成。

传统WebService与Restful

WebService和RESTful接口都是用于实现不同系统和应用之间的通信和数据交换的技术。

WebService是一种基于SOAP协议的技术,它使用XML格式来描述消息和数据,并通过HTTP协议进行传输。WebService提供了一套标准的协议和规范,包括WSDL(Web Services Description Language)和UDDI(Universal Description, Discovery, and Integration),用于描述接口和服务的结构和功能。WebService适用于复杂的企业级应用和数据交换,提供了强大的功能和灵活的扩展性,但复杂性和性能较低。

RESTful接口是一种基于REST(Representational State Transfer)原则的技术,它使用简洁的URL和HTTP方法来定义资源和操作,并使用JSON或XML等格式进行数据交换。RESTful接口遵循无状态和统一接口的原则,具有简洁和高性能的特点。RESTful接口适用于简单的Web服务和移动应用程序,具有良好的可扩展性和互操作性。

WebService的优点:

  1. 支持多种协议和数据格式:WebService可以使用不同的协议(如HTTP、SMTP、FTP等)和数据格式(如XML)进行通信和数据交换,具有较高的灵活性和可扩展性。
  2. 强大的功能和复杂的消息传输:WebService使用SOAP协议和WSDL描述服务接口,可以支持复杂的消息传输和处理,适用于复杂的企业级应用。
  3. 高度可靠和安全:WebService提供了强大的安全机制,如身份验证、加密传输等,可以确保通信和数据的安全性。
  4. 跨平台和跨语言的互操作性:WebService使用标准的Internet协议和格式,可以实现不同平台和编程语言之间的互操作性,方便不同系统之间的集成和交互。

WebService的缺点:

  1. 复杂性:WebService使用SOAP协议和XML数据格式,消息格式复杂,需要进行XML解析,增加了系统的复杂性和开发难度。
  2. 性能较低:由于SOAP消息的复杂性和XML解析的开销,WebService在性能方面相对较低,对于大规模数据传输和高并发请求可能存在性能瓶颈。
  3. 通信开销大:由于SOAP消息的冗余性和复杂性,WebService的通信开销相对较大,占用网络带宽和资源。
  4. 缺乏简洁性:由于SOAP消息的复杂性,WebService的接口设计相对较复杂,不够简洁和直观。

RESTful接口的优点:

  1. 简洁和直观:RESTful接口使用简洁的URL和HTTP方法,接口设计直观易懂,提供了良好的可读性和可维护性。
  2. 轻量级和高性能:RESTful接口使用简单的数据格式(如JSON),消息传输和解析开销较小,具有较高的性能和响应速度。
  3. 无状态和可缓存:RESTful接口是无状态的,每个请求都是独立的,不需要维护会话和状态信息,方便实现负载均衡和缓存机制,提高系统的性能和可扩展性。
  4. 易于集成和扩展:RESTful接口使用标准的HTTP协议和数据格式,可以方便地与其他系统集成,支持跨平台和跨语言的互操作性,同时也具有较好的可扩展性。

RESTful接口的缺点:

  1. 功能相对简单:相比于WebService,RESTful接口的功能相对简单,不适用于复杂的企业级应用和数据交换。
  2. 可扩展性较弱:RESTful接口使用简洁的URL和HTTP方法,扩展性相对较弱,对于复杂的消息传输和处理可能不够灵活。
  3. 缺乏标准化:RESTful接口没有像WebService那样的标准化协议和描述语言,接口设计和实现可能存在差异,不够统一和规范。

使用org.apache.cxf整合

Gradle项目增加依赖

api "org.apache.cxf:cxf-spring-boot-starter-jaxws:3.5.6"

Maven项目增加依赖

       <dependency>
           <groupId>org.apache.cxf</groupId>
           <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
           <version>3.2.4</version>
       </dependency>

服务端整合

增加webService服务配置

@Configuration
public class WebServiceConfig {

    @Autowired
    private MyService myService;

    @Autowired
    private BasicAuthInterceptor basicAuthInterceptor;

    /**
     * Apache CXF 核心架构是以BUS为核心,整合其他组件。
     * Bus是CXF的主干, 为共享资源提供一个可配置的场所,作用类似于Spring的ApplicationContext,这些共享资源包括
     * WSDl管理器、绑定工厂等。通过对BUS进行扩展,可以方便地容纳自己的资源,或者替换现有的资源。默认Bus实现基于Spring架构,
     * 通过依赖注入,在运行时将组件串联起来。BusFactory负责Bus的创建。默认的BusFactory是SpringBusFactory,对应于默认
     * 的Bus实现。在构造过程中,SpringBusFactory会搜索META-INF/cxf(包含在 CXF 的jar中)下的所有bean配置文件。
     * 根据这些配置文件构建一个ApplicationContext。开发者也可以提供自己的配置文件来定制Bus。
     */
    @Autowired
    @Qualifier(Bus.DEFAULT_BUS_ID)
    private SpringBus bus;

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, myService);
        endpoint.publish("/myService");
        //拦截器增加basic认证时校验用户名密码
        endpoint.getInInterceptors().add(basicAuthInterceptor);
        return endpoint;
    }

    /**
     * 此方法作用是改变项目中服务名的前缀名,此处127.0.0.1或者localhost不能访问时,
     * 请使用ipconfig查看本机ip来访问
     * 此方法被注释后:wsdl访问地址为http://127.0.0.1:8080/services/user?wsdl
     * 去掉注释后:wsdl访问地址为:http://127.0.0.1:8080/soap/user?wsdl
     *
     * @return
     */
    @Bean
    public ServletRegistrationBean<CXFServlet> disServlet() {
        return new ServletRegistrationBean<>(new CXFServlet(), "/webService/myservice/*");
    }
}

增加service

@WebService(targetNamespace = "http://com.demo.myservice/")
public interface MyService {

  @WebMethod
  void sayHello();

  @WebMethod
  void sayHelloToParam(final @WebParam(name = "myParam") String myParam);

}

@Service
@WebService(serviceName = "myService", targetNamespace = "http://com.demo.myservice/",
        endpointInterface = "com.ddys.gemsell.webservice.MyService")
public class MyServiceImpl implements MyService {
  @Override
  public void sayHello() {
    System.out.println("Hello");
  }

  @Override
  public void sayHelloToParam(String myParam) {
    System.out.println("Hello" + myParam);
  }
}

项目启动后访问ip:port/url url为在配置文件中配置的服务地址 即可访问webService服务

图片
可以看到对应生成的可用的SOAP服务,点击服务可看到对应生成的WSDL文件信息
在这里插入图片描述

增加Http Basic验证

在某些场景下当在访问WebService时需要携带鉴权信息,此处使用Spring Security实现Http Basic认证
增加依赖

implementation 'org.springframework.boot:spring-boot-starter-security:2.3.3.RELEASE'

Spring Security配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //禁用跨域请求伪造
        http.csrf().disable();
        //对webService服务接口进行拦截
        http.authorizeRequests().antMatchers("/webService/myservice/**").hasRole("ADMIN")
                .anyRequest().permitAll().and().httpBasic();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());;
    }
}


@Service
public class CustomUserDetailServiceImpl implements UserDetailsService {

    public static final String DEFAULT_USER = "ADMIN";
    public static final String DEFAULT_PASSWORD = "ADMIN";

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //配置角色
        List<GrantedAuthority> roles = new ArrayList<>();
        roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        //用户名匹配返回对应用户
        if (DEFAULT_USER.equals(username)) {
            return new User(username, new BCryptPasswordEncoder().encode(DEFAULT_PASSWORD), roles);
        }
        throw new UsernameNotFoundException("User not found");
    }
}

配置拦截器

@Component
public class BasicAuthInterceptor extends AbstractSoapInterceptor {


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

    //可以做身份鉴权
    @Override
    public void handleMessage(SoapMessage message) throws Fault {
     HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Basic ")) {
            String[] tokens = new String(Base64.getDecoder().decode(authHeader.substring(6))).split(":");
            String username = tokens[0];
            String password = tokens[1];
            String localusername = "ADMIN";
            String localpassword = "ADMIN";
            if (localusername.equals(username) && localpassword.equals(password)) {
                return;
            }
        }
        throw new SecurityException("Unauthorized access");
    }
}

再次访问接口
在这里插入图片描述
需要进行验证登录,输入配置的账号密码
在这里插入图片描述
可以重新访问。

客户端访问webService

动态调用

根据地址可直接访问调用

        //创建动态客户端
        JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();
        Client client = factory.createClient("http://localhost:8088/gemsell-api/webService/myservice/myService?wsdl");
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        //连接超时
        httpClientPolicy.setConnectionTimeout(2000);
        //取消块编码
        httpClientPolicy.setAllowChunking(false);
        //响应超时
        httpClientPolicy.setReceiveTimeout(120000);
        conduit.setClient(httpClientPolicy);
        try{
            Object[] objects = new Object[0];
            //第一个参数为方法名,第二个为参数
            objects = client.invoke("sayHelloToParam","123");
            System.out.println("返回数据:" + objects[0]);
        }catch (Exception e){
            e.printStackTrace();
        }

根据wsdl文件生成java代码调用

可使用maven插件根据WSDL文件生成Java代码,在使用生成的客户端代码进行服务调用

			<plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.4.6</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
 			             <sourceRoot>${project.basedir}/src/main/java/mywebservice/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
<!--									wsdl文件地址,可以使用绝对地址和类路径,-->
<!--									在使用类路径时,需要先把文件编译到classes中在进行生成-->
                                    <wsdl>classpath:mywebservice.wsdl</wsdl> 
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

执行Maven Install 生成对应的java代码
在这里插入图片描述
使用生成的代码调用服务端

        MyService_Service service = new MyService_Service();
        MyService myServiceImplPort = service.getMyServiceImplPort();
		//方法参数
        SayHelloToParam sayHelloToParam = new SayHelloToParam();
        sayHelloToParam.setMyParam("213");
		//调用方法
        myServiceImplPort.sayHello();
        myServiceImplPort.sayHelloToParam(sayHelloToParam.myParam);

设置权限信息

        MyService_Service service = new MyService_Service();
        MyService myServiceImplPort = service.getMyServiceImplPort();
        // 设置权限信息
        Map<String, Object> requestContext = ((BindingProvider) myServiceImplPort).getRequestContext();
        requestContext.put(BindingProvider.USERNAME_PROPERTY, "ADMIN");
        requestContext.put(BindingProvider.PASSWORD_PROPERTY, "ADMIN");
        SayHelloToParam sayHelloToParam = new SayHelloToParam();
        sayHelloToParam.setMyParam("213");
        myServiceImplPort.sayHello();
        myServiceImplPort.sayHelloToParam(sayHelloToParam.myParam);

问题及其他

问题

  • Spring Security默认启用了CSRF防御会导致POST请求出现403问题,所以增加

    http.csrf().disable();
    
  • 在使用maven插件使用classpath路径将WSDL文件生成Java代码过程中报错

    1. classpath protocal unknow

      先将wsdl文件编译到classes中

    2. 使用IDEA生成可能重复执行不会生成,可以重启IDEA尝试

  • 在客户端调用工程中报错请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。

    我的客户端和服务端在同一项目中出现报错,分开后调用正常

其他

可以使用SoapUI进行webService的测试和调试

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值