SpringBoot+CXF 实现简单的webservice,并支持Basic验证
pom.xml 添加的依赖
org.apache.cxf cxf-spring-boot-starter-jaxws 3.1.15
一个简单的 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日 */@WebServicepublic 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)@Componentpublic 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日 */@Configurationpublic 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中进行
测试
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 features
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("测试一下")); }}