В течение многих месяцев я занимался реализацией прокси-сервера веб-службы с использованием Mule ESB CE . Хотя для его настройки потребовалась определенная работа, сейчас она работает хорошо. В этом посте я покажу вам результат. Давайте начнем с конфигурации входящего запроса, который подписан и должен быть проверен. Кстати, я знаю, что в Mule есть также шаблон webservice-proxy-pattern, но я не мог использовать его в сочетании с подписью / проверкой, что мне нужно было сделать. Итак, вот поток, который проверяет входящий SOAP-запрос:
<flow name="order-status-update">
<https:inbound-endpoint address="${my.incoming.url}" connector-ref="httpsConnector">
<cxf:proxy-service>
<cxf:inInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<spring:bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<spring:constructor-arg>
<spring:map>
<spring:entry key="action" value="Signature" />
<spring:entry key="signaturePropFile" value="ws-validate-security.properties" />
</spring:map>
</spring:constructor-arg>
</spring:bean>
</cxf:inInterceptors>
</cxf:proxy-service>
</https:inbound-endpoint>
<https:outbound-endpoint address="${my.internal.url}" connector-ref="httpsConnector">
<cxf:proxy-client />
</https:outbound-endpoint>
</flow>
Примечание: обратите внимание, что значения ‘$ {}’ являются свойствами, считанными из файла свойств, как описано в этой статье.
Как вы можете видеть, я определил прокси-сервер CXF с двумя перехватчиками: один для ведения журнала и один для выполнения действий безопасности с использованием библиотеки Apache WSS4J . Я поставляю два параметра для WSS4JInInterceptor:
- «действие»: описывает, какое действие должно быть выполнено перехватчиком. Возможные значения: «Подпись», «Метка времени», «Зашифровать» и другие.
- ‘signaturePropFile’: параметр ссылается на файл свойств, который будет использоваться перехватчиком.
Содержимое файла ‘ws-validate-security.properties’, размещенного на пути к классам Mule, гласит следующее:
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.file=/usr/local/keystores/myKeystore.jks org.apache.ws.security.crypto.merlin.keystore.password=myPassword
Для более подробного описания этих свойств и используемой технологии см.
Эту статью .
Но это все, чтобы настроить эту часть. В этом случае я использовал Mule CE 3.2. Конфигурация mule использует следующие пространства имен:
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:https="http://www.mulesoft.org/schema/mule/https"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.2/mule.xsd
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/3.2/mule-cxf.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mulesoft.org/schema/mule/https http://www.mulesoft.org/schema/mule/https/3.2/mule-https.xsd">
Для полноты используются следующие зависимости в pom.xml:
<dependency> <groupId>org.mule</groupId> <artifactId>mule-core</artifactId> <version>3.2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.mule.modules</groupId> <artifactId>mule-module-spring-config</artifactId> <version>3.2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.mule.transports</groupId> <artifactId>mule-transport-http</artifactId> <version>3.2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.mule.modules</groupId> <artifactId>mule-module-cxf</artifactId> <version>3.2.1</version> <scope>provided</scope> </dependency>
А вот конфигурация для подписи исходящих запросов:
<flow name="place-order">
<https:inbound-endpoint address="${my.internal.url}" connector-ref="httpsConnector" responseTimeout="0">
<cxf:proxy-service enableMuleSoapHeaders="false" payload="envelope" />
</https:inbound-endpoint>
<https:outbound-endpoint address="${my.outgoing.url}" connector-ref="httpsConnector" responseTimeout="0" >
<cxf:proxy-client payload="envelope">
<cxf:outInterceptors>
<spring:bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<spring:constructor-arg>
<spring:map>
<spring:entry key="action" value="Signature" />
<spring:entry key="user" value="${signature.user}" />
<spring:entry key="signaturePropFile" value="ws-sign-security.properties" />
<spring:entry key="passwordCallbackClass" value="net.pascalalma.MyKeystorePasswordCallback"/>
<spring:entry key="signatureKeyIdentifier" value="DirectReference" />
</spring:map>
</spring:constructor-arg>
</spring:bean>
<spring:bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
</cxf:outInterceptors>
</cxf:proxy-client>
</https:outbound-endpoint>
</flow>
Важным перехватчиком здесь, конечно же, является
WSS4JOutInterceptor . Свойства, настроенные здесь:
- «действие»: описывает, какое действие должно быть выполнено перехватчиком
- ‘user’: псевдоним сертификата в криптоконфигурации подписи, которым нужно подписать сообщение. Пароль извлекается из обработчика обратного вызова.
- ‘signaturePropFile’: определяет имя файла, содержащего свойства с желаемыми настройками в нем.
- ‘passwordCallbackClass’: ссылка на обработчик обратного вызова для получения паролей для закрытых ключей в криптографических конфигурациях подписи и шифрования.
- ‘signatureKeyIdentifier’: метод вложения ключа подписи. Я хочу поместить токен непосредственно в заголовок и не использовать ссылку.
«SignaturePropFile» содержит следующую информацию:
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.file=/usr/local/keystores/myKeystore.jks org.apache.ws.security.crypto.merlin.keystore.password=myPassword
Как вы видите, я добавил пароль, который используется для открытия хранилища ключей здесь в виде простого текста. Конечно, это не способ сделать это в производственной системе, но для разработки это довольно удобно. Я использую MyKeystorePasswordCallback для получения этого пароля, как показано в следующей реализации этого класса:
package net.pascalalma;
import java.io.IOException;
import java.util.ResourceBundle;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
/**
*
* @author Pascal Alma
*/
public class MyKeystorePasswordCallback implements CallbackHandler {
private static final String BUNDLE_LOCATION = "ws-sign-security";
private static final String PASSWORD_PROPERTY_NAME = "org.apache.ws.security.crypto.merlin.keystore.password";
private static String password;
static {
final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
password = bundle.getString(PASSWORD_PROPERTY_NAME);
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
// set the password for our message.
pc.setPassword(password);
}
}
На этом завершается пример полной конфигурации для подписания и проверки запросов SOAP с помощью Mule ESB. Хотя это не ракетостроение, потребовалось довольно много времени, чтобы собрать полную конфигурацию и работать. И еще есть возможности для улучшения, в основном для обработки ошибок, но я оставлю это в другой раз
