服务端
添加依赖pom.xml
<!-- cxf:serve client -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.1.12</version>
</dependency>
<!-- 注意上楼的区别 -->
<!-- <dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>3.1.12</version>
</dependency> -->
服务接口
package com.spinach.example.webservice.cxf.server;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* web services 接口
*/
@WebService(targetNamespace = "http://server.cxf.webservice.example.spinach.com") // 命名空间,一般是接口的包名倒序
public interface CxfServices {
@WebMethod
String sayHello(@WebParam(name = "userName") String name);
}
服务接口实现
package com.spinach.example.webservice.cxf.server;
import org.springframework.stereotype.Component;
import javax.jws.WebService;
/**
* web services 接口实现
*/
@WebService(serviceName = "cxfServices"// 服务名
, targetNamespace = "http://server.cxf.webservice.example.spinach.com"// 包名倒序,并且和接口定义保持一致
, endpointInterface = "com.spinach.example.webservice.cxf.server.CxfServices") // 包名
@Component
public class CxfServicesImpl implements CxfServices {
@Override
public String sayHello(String name) {
return "hello , " + name;
}
}
发布服务配置
package com.spinach.example.webservice.cxf.server;
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
//import org.apache.cxf.endpoint.EndpointImpl;
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;
/**
* 注意引入的包路径:
* 是:org.apache.cxf.jaxws.EndpointImpl 不是:import org.apache.cxf.endpoint.EndpointImpl
* 还有:import javax.xml.ws.Endpoint;
* @author:spinach
* @date:2018年2月1日下午4:56:02
*/
@Configuration
public class CxfConfig {
@Autowired
private Bus bus;
@Autowired
private CxfServices cxfServices;
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(bus,cxfServices);
endpoint.publish("/cxfServices");//接口发布在目录下
return endpoint;
}
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
}
/* 方法二:方法二为@Autowired自动注入
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), userService());
endpoint.publish("/user");
return endpoint;
}*/
}
拦截器(增加用户名和密码需要)AuthInterceptor.java
package com.spinach.example.webservice.cxf.server;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.NodeList;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
private SAAJInInterceptor saa = new SAAJInInterceptor();
private static final String USER_NAME = "admin";
private static final String USER_PASSWORD = "pass";
public AuthInterceptor() {
super(Phase.PRE_PROTOCOL);
getAfter().add(SAAJInInterceptor.class.getName());
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
SOAPMessage mess = message.getContent(SOAPMessage.class);
if (mess == null) {
saa.handleMessage(message);
mess = message.getContent(SOAPMessage.class);
}
SOAPHeader head = null;
try {
head = mess.getSOAPHeader();
} catch (Exception e) {
logger.error("getSOAPHeader error: {}", e.getMessage(), e);
}
if (head == null) {
throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息"));
}
NodeList users = head.getElementsByTagName("username");
NodeList passwords = head.getElementsByTagName("password");
if (users.getLength() < 1) {
throw new Fault(new IllegalArgumentException("找不到用户信息"));
}
if (passwords.getLength() < 1) {
throw new Fault(new IllegalArgumentException("找不到密码信息"));
}
String userName = users.item(0).getTextContent().trim();
String password = passwords.item(0).getTextContent().trim();
if (USER_NAME.equals(userName) && USER_PASSWORD.equals(password)) {
logger.debug("admin auth success");
} else {
SOAPException soapExc = new SOAPException("认证错误");
logger.debug("admin auth failed");
throw new Fault(soapExc);
}
}
}
配置文件CxfConfig.java
package com.spinach.example.webservice.cxf.server;
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
//import org.apache.cxf.endpoint.EndpointImpl;
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;
/**
* 注意引入的包路径:
* 是:org.apache.cxf.jaxws.EndpointImpl 不是:import org.apache.cxf.endpoint.EndpointImpl
* 还有:import javax.xml.ws.Endpoint;
* @author:spinach
* @date:2018年2月1日下午4:56:02
*/
@Configuration
public class CxfConfig {
@Autowired
private Bus bus;
@Autowired
private CxfServices cxfServices;
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(bus,cxfServices);
endpoint.publish("/cxfServices");//接口发布在目录下
//打印报文日志拦截器
//endpoint.getInInterceptors().add(new LoggingInInterceptor());
//endpoint.getInInterceptors().add(new LoggingOutInterceptor());
//通过拦截器校验用户名与密码
endpoint.getInInterceptors().add(new AuthInterceptor());
return endpoint;
}
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
}
/* 方法二:方法二为@Autowired自动注入
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), userService());
endpoint.publish("/user");
return endpoint;
}*/
}
验证
## 这个地址可以看到所有方法对应的接口
http://localhost:8080/cxf/
## sayHello方法
http://localhost:8080/cxf/cxfServices?wsdl
##出现内容如下:表未服务端成功
<wsdl:definitions name="cxfServices" targetNamespace="http://server.cxf.webservice.example.spinach.com">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://server.cxf.webservice.example.spinach.com" version="1.0">
<xs:element name="sayHello" type="tns:sayHello" />
<xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />
<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="userName" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHelloResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="sayHello">
<wsdl:part element="tns:sayHello" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="sayHelloResponse">
<wsdl:part element="tns:sayHelloResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="CxfServices">
<wsdl:operation name="sayHello">
<wsdl:input message="tns:sayHello" name="sayHello">
</wsdl:input>
<wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="cxfServicesSoapBinding" type="tns:CxfServices">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document" />
<wsdl:input name="sayHello">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="cxfServices">
<wsdl:port binding="tns:cxfServicesSoapBinding" name="CxfServicesImplPort">
<soap:address location="http://localhost:8080/cxf/cxfServices" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
客户端
添加依赖的jar包:pom.xml:和服务端一样
spring-boot:依赖
<!-- cxf:serve client -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.1.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
<version>3.1.4</version>
</dependency>
<!-- 注意上楼的区别 -->
<!-- <dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>3.1.12</version>
</dependency> -->
非spring-boot依赖
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId>
<version>1.6.10</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>2.7.5</version>
</dependency>
拦截器配置(包含header信息)
新建一个ClientLoginInterceptor.java,内容如下:
package com.spinach.example.webservice.cxf.client;
import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.util.List;
public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String username;
private String password;
public ClientLoginInterceptor(String username, String password) {
//super(Phase.PREPARE_SEND);
super(Phase.WRITE);
this.username = username;
this.password = password;
}
/**
* method1
* <soap:header>
<authrity>
<username>username</username>
<password>password</username>
</authrity>
</soap:header>
* @author:spinach
* @date:2018年2月8日下午2:54:37
* @param soap
* @throws Fault
*/
/*@Override
public void handleMessage(SoapMessage soap) throws Fault {
List<Header> headers = soap.getHeaders();
Document doc = DOMUtils.createDocument();
Element auth = doc.createElement("authrity");
Element username = doc.createElement("username");
Element password = doc.createElement("password");
username.setTextContent(this.username);
password.setTextContent(this.password);
auth.appendChild(username);
auth.appendChild(password);
headers.add(0, new Header(new QName("RequestSOAPHeader"), auth));
}*/
/**
* method2
* <soap:header>
<username>username</username>
<password>password</username>
</soap:header>
* @author:spinach
* @date:2018年2月8日下午2:55:21
* @param soap
* @throws Fault
*/
@Override
public void handleMessage(SoapMessage message) throws Fault {
List<Header> headers = message.getHeaders();
headers.add(getHeader("username", this.username));
headers.add(getHeader("password", this.password));
}
/**
* 拼接头文件
* @author:spinach
* @date:2018年2月8日下午2:57:48
* @param key
* @param value
* @return
*/
private Header getHeader(String key, String value) {
QName qName = new QName("http://webservice.webxml.com.cn/", key);
Document document = DOMUtils.createDocument();
Element element = document.createElementNS("http://webservice.webxml.com.cn/", key);
element.setTextContent(value);
SoapHeader header = new SoapHeader(qName, element);
return (header);
}
}
再给个有子节点的header例子:
<soap:Header>
<AuthorizationSoapHeader xmlns="http://tempuri.org/">
<UserName>string</UserName>
<Password>string</Password>
</AuthorizationSoapHeader>
</soap:Header>
/**
* 处理soap信息
* @param message soap信息
*/
public void handleMessage(SoapMessage message) throws Fault {
// SoapHeader部分待添加的节点
QName qName = new QName("AuthorizationSoapHeader");
Document doc = DOMUtils.createDocument();
// 验证用户名
Element id = doc.createElement("UserName");
id.setTextContent("0001");
// 验证密码
Element pwd = doc.createElement("Password");
pwd.setTextContent("123");
Element root = doc.createElementNS("http://tempuri.org/","AuthorizationSoapHeader");
root.appendChild(id);
root.appendChild(pwd);
// 创建SoapHeader内容
SoapHeader header = new SoapHeader(qName, root);
// 添加SoapHeader内容
List<Header> headers = message.getHeaders();
headers.add(header);
}
调用方式CxfServicesClient.java
package com.spinach.example.webservice.cxf.client;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.junit.Test;
import com.spinach.example.webservice.cxf.server.CxfServices;
/**
* cxf:接口测试
* @author:spinach
* @date:2018年2月1日下午5:05:00
*/
public class CxfServicesClient {
/**
* 方式1.代理类工厂的方式,需要拿到对方的接口
*/
@Test
public void method1() {
try {
// 接口地址
String address = "http://localhost:8080/cxf/cxfServices?wsdl";
// 代理工厂
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置代理地址
jaxWsProxyFactoryBean.setAddress(address);
// 设置接口类型
jaxWsProxyFactoryBean.setServiceClass(CxfServices.class);
// 创建一个代理接口实现
CxfServices cs = (CxfServices) jaxWsProxyFactoryBean.create();
// 数据准备
String userName = "method1";
// 调用代理接口的方法调用并返回结果
String result = cs.sayHello(userName);
System.out.println("返回结果:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 动态调用方式
*/
public void method2() {
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient("http://localhost:8080/cxf/cxfServices?wsdl");
// 需要密码的情况需要加上用户名和密码
client.getOutInterceptors().add(new ClientLoginInterceptor("admin","pass"));
Object[] objects = new Object[0];
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("sayHello", "method2");
System.out.println("返回数据:" + objects[0]);
} catch (java.lang.Exception e) {
e.printStackTrace();
}
}
}
效果验证:运行:junit test: 打印对应的:"返回结果:hello , method1" 表示客户端成功。