SpringBoot使用cxf框架开发WebServices以及配置安全验证机制
CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding 、DataBinding、Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用。Apache CXF已经是一个正式的Apache顶级项目。CXF 支持多种 Web Services 标准,包含 SOAP、Basic Profile、WS-Addressing、WS-Policy、WS-ReliableMessaging 和 WS-Security。
目前为止cxf不支持较新的SpringBoot;支持版本为 ( SpringBoot版本 >= 1.5.0 and < 2.1.0.M1 )
服务端工程
配置 build.gradle :
compile 'org.apache.cxf:cxf-spring-boot-starter-jaxrs:3.2.5'
// https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-frontend-jaxws
compile group: 'org.apache.cxf', name: 'cxf-rt-frontend-jaxws', version: '3.2.5'
// https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-transports-http
compile group: 'org.apache.cxf', name: 'cxf-rt-transports-http', version: '3.2.5'
// https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-transports-http
compile group: 'org.apache.cxf', name: 'cxf-rt-transports-http', version: '3.2.5'
工程结构如下
服务接口的实现
package cn.cxfdemo.WebServices;
import javax.jws.WebMethod;
import javax.jws.WebService;
import java.util.List;
@WebService
public interface SaySomeThing {
@WebMethod
String getOne();
@WebMethod
List<String> getMultil();
@WebMethod
String getOneById(Integer id);
}
服务接口实现类
package cn.cxfdemo.WebServices;
import javax.jws.WebService;
import java.util.ArrayList;
import java.util.List;
/* 接口实现类名称前的注解targetNamespace是当前类实现接口所在包名称的反序(加上反斜线),endpointInterface是当前需要实现接口的全称; */
@WebService(targetNamespace = "http://WebServices.cxfdemo.cn/",endpointInterface = "cn.cxfdemo.WebServices.SaySomeThing")
public class SaySomeThingImpl implements SaySomeThing{
@Override
public String getOne() {
return "this is a person";
}
@Override
public List<String> getMultil() {
List<String> results = new ArrayList<>();
results.add("jack");
results.add("willin");
return results;
}
@Override
public String getOneById(Integer id) {
String result = "";
switch (id){
case 1 :{
result = "first";
}break;
case 2:{
result = "second";
}break;
default:{
result = "other";
}
}
return result;
}
}
服务发布类
package cn.cxfdemo.WebServices;
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.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
@Configuration
public class WebServiceConfig {
@Bean
public ServletRegistrationBean dispatcherServlet(){
return new ServletRegistrationBean(new CXFServlet(),"/WebServices/*");//发布服务名称
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus()
{
return new SpringBus();
}
@Autowired
private Bus bus;
@Bean
public SaySomeThing saySomeThingService()
{
return new SaySomeThingImpl();
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint=new EndpointImpl(springBus(), new SaySomeThingImpl());//绑定要发布的服务
endpoint.publish("/user"); //显示要发布的名称
return endpoint;
}
}
启动服务端
这样就完成了服务端的制作。
如果需要发布多个webservice,需要配置多个Config实现类文件;
客户端工程
生成客户端代码
CXF提供了根据WSDL生成客户端代码的命令wsdl2java.exe 。它是根据jdk1.7生成的本地代码,所以,有可能需要对生成的代码做一点点修改。
cxf 下载地址 http://cxf.apache.org/download.html
下载二进制压缩包;
解压后
有些朋友在这里给wsdl2java设置环境变量方便使用命令,其实不经常使用这个的话就没必要了。
C:\Users\Administrator\Downloads\apache-cxf-3.3.2\apache-cxf-3.3.2\bin>wsdl2java -d E:\\Youxun -client -impl -p cn.cxfclientdem
calhost:8080/WebServices/user?wsdl
特别注意一点:在使用wsdl2java生成客户端代码时的包路径和一定要和你的客户端工程一致;不然会出现“ServiceConstructionException“错误
参数 -p 指定包路径
生成的客户端代码:
附wsdl2java用法:
wsdl2java -p com -d D:\src -all xx.wsdl
-p 指定其wsdl的命名空间,也就是要生成代码的包名:
-d 指定要产生代码所在目录
-client 生成客户端测试web service的代码
-server 生成服务器启动web service的代码
-impl 生成web service的实现代码
-ant 生成build.xml文件
-all 生成所有开始端点代码:types,service proxy,service interface, server mainline, client mainline, implementation object, and an Ant build.xml file.
编写客户端代码
build.gradle 添加依赖与服务端一致
将wsdl2java生成的代码放到工程里
客户端调用代码
package cn.cxfclientdemo.Controller;
import cn.cxfclientdemo.WebServices.SaySomeThing;
import cn.cxfclientdemo.WebServices.SaySomeThingImplService;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class AccessWSController {
// 第一种调用方式:动态调用
@RequestMapping("/one")
public String getOneProc(){
JaxWsDynamicClientFactory dcflient= JaxWsDynamicClientFactory.newInstance();
Client client=dcflient.createClient("http://localhost:8080/WebServices/user?wsdl");
try {
Object[] results= client.invoke("getOne", "");
return results[0].toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 调用方式二,通过接口协议获取数据类型
@RequestMapping("/multi")
public List<String> getmulti(){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean=new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("http://localhost:8080/WebServices/user?wsdl");
jaxWsProxyFactoryBean.setServiceClass(SaySomeThing.class);
SaySomeThing saySomeThing=(SaySomeThing) jaxWsProxyFactoryBean.create();
List<String> results = saySomeThing.getMultil();
results.forEach((i)->{
System.out.println(i);
});
return results;
}
// 调用方式三,通过接口协议获取数据类型,设置链接超时和响应时间
@RequestMapping("/byid")
public String getmulti(Integer id){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean=new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("http://localhost:8080/WebServices/user?wsdl");
jaxWsProxyFactoryBean.setServiceClass(SaySomeThing.class);
SaySomeThing saySomeThing=(SaySomeThing) jaxWsProxyFactoryBean.create();
Client proxy= ClientProxy.getClient(saySomeThing);
HTTPConduit conduit=(HTTPConduit)proxy.getConduit();
HTTPClientPolicy policy=new HTTPClientPolicy();
policy.setConnectionTimeout(1000);
policy.setReceiveTimeout(1000);
conduit.setClient(policy);
String results = saySomeThing.getOneById(id);
return results;
}
}
因为我的这个服务端和客户端运行在一台机器上为了避免端口号冲突,把客户端的端口改为8081
测试客户端
ok,三个方法都成功运行了。
给WebServices增加安全方案
第一种:在调用API时增加一两个参数验证用户身份;
public Json wsFunc(param , userid , password);
第二种:使用ws-security框架,网上不少的关于此类框架的资料我就不在这赘述了。