WebService的测试程序开发
CXF 中包含了一个 Maven 插件 cxf-codegen-plugin,能够将 Wsdl 文件动态生成 webservice 本地类。下面针对Apace cxf 进行介绍如何配置,以及webservice中的几种常见安全验证方式。
Apache CXF简介
Apache CXF = Celtix + XFire。Apache CXF 是一个开源的 Services 框架,CXF 利用 Frontend 编程 API 来构建和开发 Web Services , 支持 SOAP, XML/HTTP, RESTful HTTP, 和 CORBA ,并且可以在多种协议上运行,如HTTP, JMS 和 JBI.
官方地址:http://cxf.apache.org/
将WSDL文件转化为Java代码
CXF 中包含了一个 Maven 插件 cxf-codegen-plugin,能够将 Wsdl 文件动态生成 webservice 本地类。 具体方法:在eclipse中建立maven项目,然后将pom中增加配置如下,然后运行mvn run,mvn install, mvn test等,即可生成代码。
详细的pom.xml配置文件见最下方附注。
Maven 配置
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>http://172.0.0.1:8080/myService.wsdl</wsdl>
</wsdlOption>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/myService.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
使用上述maven插件,代码会自动生成客户端,其名称类似于class ServiceName_ServicePort_Client,不过自动生成的客户端代码比较繁琐,不便于维护,建议自己重新建立自己的测试用例。copy艳超的代码如下:
@Test
public void blurNameTest(){
//实例化接口
CompanyRemote companyRemoteClient = new CompanyRemoteImplService().
//调用接口中的方法 getCompanyRemoteImplPort();
companyList = companyRemoteClient.getAllCompanysByBlurName("百度");
System.out.println(companyList.size());
}
WebService客户端的安全认证
WebService客户端与服务器端进行通信时,经常需要在soap文件中增加安全认证信息。在我的项目中,涉及到了2种安全认证方式:
1. 在soap文件的header中增加用户名和密码校验
<soap:Envelope>
<soapenv:Header>
<wsse:Security ...>
<wsse:UsernameToken wsu:Id="UsernameToken-1">
<wsse:Username>test</wsse:Username>
<wsse:Password Type=".."...>123456</wsse:Password>
<wsse:Nonce EncodingType="...">4AJ7sPA8NRQ74faqignO3g==</wsse:Nonce>
<wsu:Created>2013-06-20T12:33:05.001Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soap:Envelope>
方法为建立一个拦截器
import org.apache.ws.security.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: shenyanchao
* Date: 5/15/13
* Time: 2:00 PM
*/
public class WsClientAuthHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
pc.setIdentifier("test");
pc.setPassword("123456");// ▲【这里必须设置密码】▲
}
}
}
然后在客户端中添加该拦截器
FeeFinanceService_Service server=new FeeFinanceService_Service();
String username = "test";//服务器端分配的用户名
String password = "123456";//服务器端分配的密码
FeeFinanceService port = server.getFeeFinanceServicePort();
Client client = ClientProxy.getClient(port);
Map<String, Object> props = new HashMap<String, Object>();
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
props.put(WSHandlerConstants.USER, "cxfclient");
props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
props.put(WSHandlerConstants.PW_CALLBACK_CLASS, WsClientAuthHandler.class.getName());
WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);
client.getOutInterceptors().add(wss4jOut);
List<JtReq> reqlist = new ArrayList<JtReq>();
JtReq req = new JtReq();
req.setBusinessNumb("T");
2. 安全验证在soap中插入,但是实际发送soap文件时会出现在html的头文件中
POST /cmssoap/ws/fee_biz_service HTTP/1.1[
][
]
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction:
Authorization: Basic dGVzdDoxMjM0NTY=
Content-Length: 1475
Host: 10.237.4.242:8900
<soap:Envelope>
...
</soap:Envelope>
该程序,可以直接在函数中增加授权验证
public void testInsert() throws ValidationException_Exception {
String username = "test";//服务器端分配的用户名
String password = "123456";//服务器端分配的密码
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("test",
"123456".toCharArray());
}
});
FeeBizService_Service server=new FeeBizService_Service();
FeeBizService port = server.getFeeBizServicePort();
BindingProvider bp = (BindingProvider)port;
Map<String,Object> context = bp.getRequestContext();
context.put(BindingProvider.USERNAME_PROPERTY, username);
context.put(BindingProvider.PASSWORD_PROPERTY, password);
List<Fee> fees = new ArrayList<Fee>();
String str = port.sendFee(fees);
3. 在soap的header中直接增加用户名和密码
<soap:Envelope>
<soapenv:Header>
<wsse:Username>youthflies</wsse:Username>
<wsse:Password>youthflies</wsse:Password>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soap:Envelope>
建立拦截器
/**
* @author youthflies
* 自定义的soap拦截器,用来添加header信息
*/
public class SoapHeaderInterceptor extends AbstractSoapInterceptor
{
public SoapHeaderInterceptor()
{
super(Phase.WRITE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault
{
// TODO Auto-generated method stub
List headers=message.getHeaders();
headers.add(getHeader("username", "youthflies"));
headers.add(getHeader("password", "youthflies"));
}
//http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
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);
}
}
case中,添加拦截器:
//实例化接口实现类
MobileCodeWS mobileCodeWS = new MobileCodeWS();
//实例化接口
MobileCodeWSSoap mobileCodeWSSoap = mobileCodeWS.getMobileCodeWSSoap();
Client client = ClientProxy.getClient(mobileCodeWSSoap);
client.getOutInterceptors().add(new SoapHeaderInterceptor());
//调用接口中的方法
System.out.println(mobileCodeWSSoap.getMobileCodeInfo("13898767654", ""));
附录一:
SVN上生成webservice测试文件的代码路径:(请忽略SVN地址,外面的人没法用)
https://xxx.xxx.xxx/myspace/iteqa/InterfaceTest/cxf4ws
生成的代码会在src/main/java路径下面,而自己的测试代码可以放到src/test下面,提交到SVN时,只提交src/test下的文件即可。
完整的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yourGroupId</groupId>
<artifactId>yourArtifactId</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<cxf.version>2.7.3</cxf.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>${cxf.version}</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-bindings-soap</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<executable>javac</executable>
<compilerVersion>1.6</compilerVersion>
<fork>true</fork>
<verbose>true</verbose>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.sourceDirectory}</sourceRoot>
<defaultOptions>
<extraargs>
<extraarg>-impl</extraarg>
<extraarg>-verbose</extraarg>
<extraarg>-validate</extraarg>
<!--<extraarg>-client</extraarg>-->
</extraargs>
</defaultOptions>
<wsdlOptions>
<!--<wsdlOption>
<wsdl>http://itebeta.xxx.com:8102/webservice/CompanyService?wsdl</wsdl>
</wsdlOption>
<wsdlOption>
<wsdl>http://itebeta.xxx.com:8102/webservice/CompanyServiceN?wsdl</wsdl>
</wsdlOption>
<wsdlOption>
<wsdl>http://itebeta.xxx.com:8102/webservice/UserServiceN?wsdl</wsdl>
</wsdlOption>-->
<wsdlOption>
<wsdl>http://xxx.xxx.4.242:8900/cmssoap/ws/fee_finance_service?wsdl</wsdl>
<extraargs>
<!--<extraarg>-exsh</extraarg>
<extraarg>true</extraarg>-->
<!--<extraarg>-all</extraarg>-->
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.cxf
</groupId>
<artifactId>
cxf-codegen-plugin
</artifactId>
<versionRange>
[2.1.4,)
</versionRange>
<goals>
<goal>wsdl2java</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>