SpringBoot+CXF 实现简单的webservice,并支持Basic验证
pom.xml 添加的依赖
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.1.15</version>
</dependency>
一个简单的 interface 标记为webservice接口
package com.wzh.demo.webservice.service;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* @desc: 测试发布的websercice服务
* @author: wzh
* @date: 2019年3月31日
*/
@WebService
public interface IhelloService {
/**
* @desc: 测试发布的websercice接口
* @author: wzh
* @date: 2019年3月31日
* @param message
* @return
*/
@WebMethod
public String helloWebService(@WebParam(name="msg")String message);
}
basic鉴权拦截器
CXF内置了很多拦截器,大部分默认添加到拦截器链中,有些拦截器也可以手动添加,如CXF的日志拦截器。如果需要自定义拦截器,只要继承AbstractPhaseInterceptor或者AbstractPhaseInterceptor的子类(如AbstractSoapInterceptor)
package com.wzh.demo.webservice.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.util.Base64;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
/**
* @desc: 测试用baisc鉴权拦截器
* @author: wzh
* @date: 2019年3月31日
*/
public class HelloAuthInterceptor extends AbstractSoapInterceptor{
private static final String BASIC_PREFIX = "Basic ";
private static final String USERNAME = "admin";
private static final String PASSWORD = "123456";
/**
* 指定加入拦截器到某个阶段
*/
public HelloAuthInterceptor() {
super(Phase.PRE_INVOKE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
// 获取HttpServletRequest
HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
String auth = request.getHeader("Authorization");
if (auth == null) {
SOAPException exception = new SOAPException("Authorization 授权信息为空!");
throw new Fault(exception);
}
if (!auth.startsWith(BASIC_PREFIX)) {
SOAPException exception = new SOAPException("Authorization 非baisc验证");
throw new Fault(exception);
}
// 合法的baisc格式为username:password 例如:admin:123456
String plaintext = new String(Base64.decodeBase64(auth.substring(BASIC_PREFIX.length())));
if (StringUtils.isEmpty(plaintext) || !plaintext.contains(":")) {
SOAPException exception = new SOAPException("Authorization 非baisc验证");
throw new Fault(exception);
}
String[] usernameAndPass = plaintext.split(":");
String username = usernameAndPass[0];
String password = usernameAndPass[1];
if (!USERNAME.equals(username) || !PASSWORD.equals(password)) {
SOAPException exception = new SOAPException("用户名或密码不正确!");
throw new Fault(exception);
}
}
}
webservice 服务接口实现类
package com.wzh.demo.webservice.service.impl;
import java.util.Date;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import org.apache.cxf.interceptor.InInterceptors;
import org.springframework.stereotype.Component;
import com.wzh.demo.webservice.service.IhelloService;
@InInterceptors(interceptors={"com.wzh.demo.webservice.interceptor.HelloAuthInterceptor"})// 添加拦截器
@WebService
@SOAPBinding(style=Style.RPC)
@Component
public class HelloServiceImpl implements IhelloService {
@Override
public String helloWebService(String message) {
System.out.println(message);
return "响应:" + new Date();
}
}
配置发布webservice服务
package com.wzh.demo.webservice.Config;
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.cxf.transport.servlet.CXFServlet;
import com.wzh.demo.webservice.service.IhelloService;
/**
* @desc: CXF SOAP 接口配置发布
* @author: wzh
* @date: 2019年3月31日
*/
@Configuration
public class SoapConfig {
@Autowired
@Qualifier(Bus.DEFAULT_BUS_ID)
private SpringBus bus;
@Autowired
private IhelloService ihelloService;
/**
* 改变项目中服务名的前缀名
* 方法被注释后:wsdl访问地址为http://localhost:8080/SpringBootDemo_eclipse/services/ihelloService?wsdl
* 去掉注释后:wsdl访问地址为http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl
* @return
*/
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap/*");
}
// 发布多个接口 添加多个@Bean endpoint.publish 这里不能一样
@Bean
public Endpoint helloEndpoint() {
EndpointImpl endpoint = new EndpointImpl(bus, ihelloService);
endpoint.publish("/ihelloService");
return endpoint;
}
}
如果是传统的spring+cxf 项目,拦截器的写法是一样的,只是发布和配置的方法在cxf的配置文件的xml中进行
<!--id:名称(随意配),implementor:指定接口具体实现类,address:随意配-->
<jaxws:endpoint id="helloWorld" implementor="#HelloWorldImpl" address="/HelloWorld" >
<!-- 输入日志拦截器 -->
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
</jaxws:inInterceptors>
<!-- 输出日志拦截器 -->
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
</jaxws:outInterceptors>
<jaxws:properties>
<entry key="mtom_enabled" value="true"></entry>
</jaxws:properties>
</jaxws:endpoint>
测试
WSDL 地址:http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl
代码客户端测试
CXF 自动编译解析的客户端
package com.wzh.demo.webservice.client;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
/**
* This class was generated by Apache CXF 2.6.2
* 2019-03-31T19:54:58.255+08:00
* Generated source version: 2.6.2
*
*/
@WebService(targetNamespace = "http://service.webservice.demo.wzh.com/", name = "IhelloService")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface IhelloService {
@WebResult(name = "return", targetNamespace = "http://service.webservice.demo.wzh.com/", partName = "return")
@WebMethod
public java.lang.String helloWebService(
@WebParam(partName = "msg", name = "msg")
java.lang.String msg
);
}
package com.wzh.demo.webservice.client;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.Service;
/**
* This class was generated by Apache CXF 2.6.2
* 2019-03-31T19:54:58.261+08:00
* Generated source version: 2.6.2
*
*/
@WebServiceClient(name = "HelloServiceImplService",
wsdlLocation = "http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl",
targetNamespace = "http://impl.service.webservice.demo.wzh.com/")
public class HelloServiceImplService extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://impl.service.webservice.demo.wzh.com/", "HelloServiceImplService");
public final static QName HelloServiceImplPort = new QName("http://impl.service.webservice.demo.wzh.com/", "HelloServiceImplPort");
static {
URL url = null;
try {
url = new URL("http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl");
} catch (MalformedURLException e) {
java.util.logging.Logger.getLogger(HelloServiceImplService.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl");
}
WSDL_LOCATION = url;
}
public HelloServiceImplService(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public HelloServiceImplService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public HelloServiceImplService() {
super(WSDL_LOCATION, SERVICE);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public HelloServiceImplService(WebServiceFeature ... features) {
super(WSDL_LOCATION, SERVICE, features);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public HelloServiceImplService(URL wsdlLocation, WebServiceFeature ... features) {
super(wsdlLocation, SERVICE, features);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public HelloServiceImplService(URL wsdlLocation, QName serviceName, WebServiceFeature ... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* @return
* returns IhelloService
*/
@WebEndpoint(name = "HelloServiceImplPort")
public IhelloService getHelloServiceImplPort() {
return super.getPort(HelloServiceImplPort, IhelloService.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns IhelloService
*/
@WebEndpoint(name = "HelloServiceImplPort")
public IhelloService getHelloServiceImplPort(WebServiceFeature... features) {
return super.getPort(HelloServiceImplPort, IhelloService.class, features);
}
}
客户端basic 认证调用
package com.wzh.demo.webservice.client;
import javax.xml.ws.BindingProvider;
/**
* @desc: 测试连接用客户端
* @author: wzh
* @date: 2019年3月31日
*/
public class testClient {
public static void main(String[] args) {
IhelloService service = new HelloServiceImplService().getHelloServiceImplPort();
// 添加basc 认证
BindingProvider bp = (BindingProvider) service;
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "admin");
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "1234562");
System.out.println(service.helloWebService("测试一下"));
}
}