В течение многих месяцев я занимался реализацией прокси-сервера веб-службы с использованием 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. Хотя это не ракетостроение, потребовалось довольно много времени, чтобы собрать полную конфигурацию и работать. И еще есть возможности для улучшения, в основном для обработки ошибок, но я оставлю это в другой раз