Документации не сказать чтобы совсем никакой, но по части WS_Security очень скудная, и не понятно, как же в итоге заставить клиента веб-сервиса работать. Забегая вперед скажу, что у меня так и не получилось создать запрос, который был бы принят сервером.
Вот пример попытки вызова метода веб-сервиса.
Client client = ClientProxy.getClient(iface);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
Endpoint cxfEndpoint = client.getEndpoint();
cxfEndpoint.put("ws-security.encryption.username", "server");
cxfEndpoint.put("ws-security.encryption.properties", "enc.properties");
cxfEndpoint.put("ws-security.signature.username", "mykey");
cxfEndpoint.put("ws-security.signature.properties", "sig.properties");
cxfEndpoint.put("ws-security.callback-handler", "ClientKeystorePasswordCallback");
sig.properties и enc.properties идентичны и содержат следующее:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=changeit
org.apache.ws.security.crypto.merlin.keystore.file=/home/grigory/out.jks
Оказалось, что в формируемом клиентом SOAP запросе в заголовоке wsse:Security нет тега wsse:BinarySecurityToken. Я понял это только после того, как с помощью Glassfish Metro создал работающего клиента и сравнил отправляемые запросы.
Следующей на очереди была Glassfish Metro. Мне она изначально не нравилась из-за того, что заточена под использование в NetBeans, и из-за этого вся документация подается в виде "пойдите туда, нажмите эту кнопочку и все волшебным образом заработает". Но в итоге все-таки удалось создать решение, не зависящее от конкретной IDE, но, к сожалению, заставляющее программиста вручную редактировать xml, если в wsdl сервиса появятся изменения.
Итак, вот решение:
- копируем wsdl и все зависимые wsdl и xsd файлы локально. В нашем случае это service.wsdl и wsdl0.wsdl. В wsdl0.wsdl описываются полиси для каждого метода, впоследствии мы их заменим.
- дописываем к названию wsdl файлов .xml (и меняем внутри, если есть импорты)
- создаем файл wsit-client.xml (он должен называться именно так) следующего содержания, в который просто включаем наш переименованный service.wsdl.xml
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
name="mainclientconfig">
<import location="service.wsdl.xml"
namespace="http://testuri.com/wcflib/"/>
</definitions>
- заменяем в файле wsdl0.wsdl.xml все wsp:Policy для всех методов на одну-единственную, следующего вида:
...
xmlns:wsp1="http://www.w3.org/ns/ws-policy"
...
/>
<wsp1:Policy wsu:Id="WS2007HttpBinding_IWebService_policy">
<wsp1:ExactlyOne>
<wsp1:All>
<sc:KeyStore wspp:visibility="private" alias="${keystore.private}"
storepass="${keystore.password}" type="JKS"
location="${keystore.path}" keypass="${keystore.password}"/>
<sc:TrustStore wspp:visibility="private" peeralias="${keystore.server}"
storepass="${keystore.password}" type="JKS"
location="${keystore.path}"/>
</wsp1:All>
</wsp1:ExactlyOne>
</wsp1:Policy>
${keystore.path}, ${keystore.password} и т.п проставятся на этапе сборки.
WS2007HttpBinding_IWebService_policy нужно заменить на значение, прописанное в теге wsp:PolicyReference в wsdl:binding для вашего сервиса
- Удаляем все ссылки на несуществующие полиси. Сами полиси мы удалили на предущем шаге, но ссылки в wsdl:input и wsdl:output на них остались.
- Генерируем код клиента. Этот шаг мог быть первым, он не зависит от предыдущих. Для этого используем плагин для maven.
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<extension>true</extension>
<bindingDirectory>
src/main/resources
</bindingDirectory>
<bindingFiles>
<bindingFile>jaxb-java-datatypes.xml</bindingFile>
<bindingFile>jaxb-binding-xsd4.xml</bindingFile>
<bindingFile>jaxb-binding-xsd9.xml</bindingFile>
</bindingFiles>
<wsdlDirectory>
src/main/resources
</wsdlDirectory>
<wsdlFiles>
<wsdlFile>service.wsdl</wsdlFile>
</wsdlFiles>
<wsdlLocation>http://testuri.com/wcflib-tc/service.svc</wsdlLocation>
<sourceDestDir>
${basedir}/target/generated-sources
</sourceDestDir>
</configuration>
</execution>
</executions>
</plugin>
Также очень полезным оказалось автоматическая генерация метода toString(). Этот метод генерируется другим плагином, поскольку я не нашел способа передать нужные мне параметры в XJC в jaxws-maven-plugin.
Ниже приведен сконфигурированный maven-jaxb2-plugin; указание forceRegenerate=true обязательно, иначе плагин ничего не будет делать
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.7.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<generateDirectory>
${basedir}/target/generated-sources
</generateDirectory>
<args>
<arg>-XtoString</arg>
<arg>-b</arg>
<arg> src/main/resources/jaxb-binding-xsd4.xml</arg>
<arg>-b</arg>
<arg> src/main/resources/jaxb-binding-xsd9.xml</arg>
<arg>-b</arg>
<arg> src/main/resources/jaxb-java-datatypes.xml</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.5.3</version>
</plugin>
</plugins>
<forceRegenerate>true</forceRegenerate>
</configuration>
</plugin>
- После того, как клиентские классы были сгенерированы, можно вызывать методы веб-сервиса. В нижеприведенном примере проставляются таймауты и указывается расположение wsdl.
System.setProperty("sun.net.client.defaultConnectTimeout", "5000");
System.setProperty("sun.net.client.defaultReadTimeout", "50000");
URL baseUrl = TimeoutTest.class.getResource(".");
String WEBSERVICE_WSDL_LOCATION = "service.wsdl";
GetCountriesRequestMessage r = new GetCountriesRequestMessage();
WebService service = new WebService(new URL(baseUrl, WEBSERVICE_WSDL_LOCATION), new QName("http://testuri.com/wcflib/", "WebService"));
IWebService iface = service.getWS2007HttpBindingIWebService();
iface.getCountries(r);
}catch (WebServiceException ex){
if(ex.getCause() instanceof SocketTimeoutException){
if(ex.getCause().getMessage().contains("connect timed out")){
System.out.println("Connection timeout");
}else{
System.out.println("READ timeout! " + ex.getCause().getMessage());
}
}else if(ex.getCause() instanceof UnknownHostException) {
System.out.println("No route to host! " + ex.getCause().getMessage());
}else{
ex.printStackTrace();
}
}catch (Exception ex){
ex.printStackTrace();
}
В случае правильной конфигурации при запуске тестов в логе появится следующее:
Apr 30, 2013 2:53:59 PM [com.sun.xml.ws.policy.parser.PolicyConfigParser] parse
INFO: WSP5018: Loaded WSIT configuration from file: file:/home/grigory/dev/metro/target/test-classes/wsit-client.xml.
Apr 30, 2013 2:54:01 PM com.sun.xml.ws.security.opt.impl.util.CertificateRetriever setServerCertInTheContext
INFO: WSS0824: The certificate found in the server wsdl or by server cert property is valid, so using it
Комментариев нет:
Отправить комментарий