Статьи

Подписание и проверка запросов на мыло с помощью Mule ESB

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