Некоторое время назад я хотел посмотреть, как легко написать прокси для веб-служб (wsproxy) с помощью Spring Web Services. Итак, я думал, что поделюсь результатом на Github. Не стесняйтесь использовать его (лицензия Apache v2) или позвольте ему служить основой для вашей собственной разработки. В оставшейся части статьи будет рассказано о том, как я использовал веб-сервисы Spring для его создания, а также будет дано краткое руководство по использованию текущей реализации.
Wsproxy в этом контексте является центральным уровнем доступа, поддерживающим мыло, который передает сообщения между системами. Эта ретрансляция работает в двух направлениях; для служб, размещенных внутри (входящий режим), а также для служб, размещенных извне, где мы являемся клиентом (исходящий режим). Можно сравнить это с традиционным http-прямым / обратным прокси-сервером, но вместо того, чтобы работать с транспортом приложения, он поднимается на один уровень выше в стеке и обрабатывает сообщения приложения; в этом случае мыло.
В исходящем режиме наши внутренние службы будут (обычно) использовать wsproxy в качестве http-прокси-сервера. Затем он будет заниматься доставкой полученного сообщения фактической цели. Для входящего режима модуль будет действовать как обратный прокси-сервер, принимая входящие сообщения от внешних клиентов и передавая их нашим внутренним службам.
Прежде чем продолжить, несколько слов о прямых / обратных прокси:
Прямой прокси — это то, что клиент явно настраивает в дополнение к указанию целевого URL. Стек http клиента отправит сообщение настроенному прокси и отправит хост / порт фактического желаемого целевого хоста через заголовки http (заголовок хоста). Затем прокси-сервер обрабатывает запрос на целевой хост / порт и возвращает ответ клиенту. Таким образом, прокси-сервер формирует хост и порт для целевого URL из заголовка http Host. Для пути он будет использовать путь из запроса, полученный от клиента. Обратный прокси-сервер ведет себя (с точки зрения клиента) как фактический целевой сервер. Клиент не знает ни о каких прокси и не должен настраивать ничего особенного. Клиент использует в качестве целевого URL-адреса URL-адрес обратного прокси-сервера. Обратный прокси-сервер сможет перехватить сообщение от клиента и переслать его фактической цели в сети. Обратный прокси-сервер потребует дополнительной настройки, например, URL (хост, порт и, возможно, путь) целевой службы, поскольку хост / порт не может быть выведен из запроса или заголовков http.
При использовании этого модуля все еще можно реализовать реальный обратный прокси-сервер http на границах для разгрузки TLS или других транспортных целей. Для внешнего входящего трафика (сообщения, поступающие от внешних клиентов, предназначенных для внутренней службы) модуль wsproxy будет просто вторым обратным прокси-сервером в строке для входящих запросов. Для внутреннего исходящего трафика (сообщения от внутренних клиентов, предназначенные для внешних конечных точек) URL-адрес обратного прокси-сервера http будет настроен как целевой URL-адрес для этой конкретной службы в модуле wsproxy.
Одной из особенностей наличия такого wsproxy, осведомленного о мыле, является централизация проблем. Цель состоит в том, чтобы мыльные потоки, проходящие через, могли быть перехвачены. Таким образом, мы можем реализовать такие вещи, как: ведение журнала аудита, мониторинг, контроль доступа, безопасность сообщений,… делая это, мы создаем централизованное место для удовлетворения этих требований вместо необходимости повторной реализации их для каждого приложения.
Я выбрал веб-сервисы Spring, потому что нет сложной инфраструктуры или дизайна для понимания. Он также очень расширяемый и предлагает повторное использование на нужном уровне для наших требований.
Однако в настоящее время я должен также указать, что существуют существующие решения, которые могут сделать это также и многое другое. Они часто называются шлюзами безопасности XML и представляют собой программный пакет или полностью оборудованные устройства. Как всегда, вы должны перевесить преимущества этих существующих решений по сравнению с написанием чего-либо самостоятельно. Факт заключается в том, что они не приходят бесплатно (если не сказать больше), и вам все еще нужен кто-то с необходимыми навыками для их настройки и обслуживания. Как мы увидим, наши требования легко выполнить с помощью небольшого кода (и помощи веб-служб Spring), что дает нам весь необходимый нам контроль.
В соответствии с требованиями, я имел в виду простоту расширения дизайна с учетом следующих требований:
- Исходящий режим должен быть свободным от конфигурации для стандартного использования. Это означает, что при необходимости доступа к новым внешним службам наш прокси-сервер должен ретранслировать сообщения, не требуя дополнительной настройки.
- Сообщения, проходящие через шлюз, должны быть зарегистрированы. Без дополнительной настройки все сообщение должно быть зарегистрировано по умолчанию. При желании нам нужно иметь возможность более детально настроить, какие части необходимо регистрировать для определенных услуг
- Для (в основном внешней) исходящей связи мы должны иметь возможность настроить прямой прокси-сервер или переопределить цель с помощью предварительно настроенного хоста (существующего обратного прокси-сервера или фактической конечной точки)
- Модуль должен иметь возможность пересылать сообщения через безопасный транспорт в случае отсутствия внешнего обратного прокси-сервера для разгрузки исходящего безопасного транспорта. Для входящего безопасного транспорта мы разрешим это обрабатывать контейнером, в котором работает модуль; так что это выходит за рамки модуля
- Уметь применять и обрабатывать целостность / конфиденциальность сообщений
Конструкция компонента выглядит следующим образом:
Есть 3 основных компонента. Конечная точка ( конечная точка перехвата всех на чертеже), которая будет действовать как получатель сообщения, отправляемого в wsproxy. Экспедитор , который передаст сообщение цели. Наконец, цепочки перехватчиков — это ловушки, с помощью которых мы можем перехватывать отправляемые / получаемые сообщения и что-то с ними делать.
Эти 3 компонента предлагаются Spring Web Services; конечной точкой является org.springframework.ws.server.endpoint.annotation.Endpoint, реализующий org.springframework.ws.server.endpoint.MessageEndpoint, чтобы иметь возможность получать необработанные полезные данные . Экспедитор использует org.springframework.ws.client.core.WebServiceTemplate, а цепочки перехватчиков — org.springframework.ws.client.support.interceptor.ClientInterceptor и / или org.springframework.ws.server.EndpointInterceptor в зависимости от того, какие стороны требуют перехватчика функционировать (подробнее об этом позже). Для безопасности сообщений мы будем использовать WSS4J, но это всего лишь реализация перехватчика, а не нового компонента.
Важно понимать, что есть две цепочки перехватчиков. С точки зрения wsproxy мы назовем первую «входящей цепочкой». Это тот, который работает между клиентом и wsproxy. «Исходящая цепочка» — это та, которая действует между wsproxy и конечной точкой назначения. Итак, если у нас есть внутренний клиент, обращающийся к внешней конечной точке через наш wsproxy, входящая цепочка будет вызвана, когда сообщение получено wsproxy. Исходящая цепочка будет вызываться с того момента, как wsproxy пересылает сообщение на целевую конечную точку. Spring имеет два интерфейса, чтобы различать, на какой «стороне» работает перехватчик (перехватчик также может реализовывать оба интерфейса, что позволяет ему функционировать с обеих сторон). Org.springframework.ws.server.EndpointInterceptor работает на стороне конечной точки, для wsproxy это является входящим. Org.springframework.ws.client.support.interceptor.ClientInterceptor работает на стороне клиента, поэтому для wsproxy это является исходящим. Btw; мы используем входящие и исходящие, а не исходные имена Spring (клиент / конечная точка), чтобы избежать путаницы. Как вы уже заметили, wsproxy также является конечной точкой и клиентом. Тем не менее, когда мы ссылаемся на «клиента», мы имеем в виду фактического клиента службы, а «конечная точка» является фактической целевой службой.
Сам модуль будет работать в стандартном контейнере сервлетов JEE, как ваше типичное приложение Spring. Для всего входящего трафика используется коннектор http (или https) из контейнера. Для всего исходящего трафика используется WebServiceTemplate с настроенным общедоступным httpclient, который мы сможем использовать для http и https, если потребуется. Идентификация услуги выполнена в стиле «доклит». Это означает, что мы берем первый элемент тела, включая его пространство имен. Это представляется как QName . Эта идентификация важна, так как у нас будет конфигурация для каждой услуги, например, для прямого прокси, протокола пересылки, сопоставления URL-адресов конечных точек, определенных регистраторов и т. Д.
Хорошо, этого достаточно. давайте возьмем этого ребенка на спину! Импортируйте проект в выбранной вами IDE, убедитесь, что вы импортировали его как проект Maven, поскольку Maven должен будет отфильтровать файл active_environment.properties (это делается автоматически через профиль по умолчанию). Тогда мы будем:
- Настройте обычную конечную точку на основе мыла
- Развернуть wsproxy
- Используйте клиент веб-служб для доступа к конечной точке через прокси-модуль
Для начальной загрузки простой конечной точки в источниках тестов предусмотрен класс SimpleEndpoint, использующий внутренний JAX-WS JDK и сервер http для начальной загрузки конечной точки веб-службы :
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class SimpleEndpoint { public static void main(String args[]) { } @WebService public static class SimpleWebServiceEndpoint { public Date getCurrentDate(String randomParameter) { return new Date(); } } } |
Просто запустите это как новое Java-приложение, оно будет работать до тех пор, пока процесс не будет остановлен. Загрузите wsproxy, развернув проект на выбранном вами сервере (я буду использовать Tomcat7), дополнительная настройка не требуется. Что касается клиента, мы будем использовать soap-ui (вы также можете использовать cURL, если хотите). В мыльном интерфейсе мы сначала должны создать проект. Мы делаем это на основе WSDL, предоставляемого нашим тестовым сервисом (доступным по адресу http: // localhost: 9999 / simple? WSDL). Далее нам нужно настроить наш модуль wsproxy в качестве прямого http-прокси в soap-ui:
Проекты soap-ui также доступны в проекте, если хотите. Не забудьте включить настройки прокси, как описано выше, они не сохраняются как часть проекта.
Важно: не забудьте снова отключить настройки прокси, если вы начинаете новый проект. soap-ui будет использовать настройки прокси для стандартного http-трафика, а не только для soap / http. Например; при создании нового проекта на основе URL-адреса WSDL soap-ui будет также использовать настройки прокси-сервера http для получения WSDL. Поскольку модуль wsproxy не является чистым http-прокси (а вместо него мыльным), он не пропускает трафик, не связанный с мылом.
Последнее, что нам нужно настроить, это целевой URL в soap-ui. Модуль wsproxy по умолчанию (по крайней мере на tomcat) развернут в корне контекста, названном в честь имени файла. В нашем случае это означает, что модуль доступен по адресу: http: // localhost: 8080 / ws-proxy /
Есть два варианта:
- Вместо этого разверните модуль под корнем сервера приложений (/). В этом случае ничего не нужно менять на целевой URL. Целевой URL останется тем же, что и при использовании без прокси-модуля.
- Используйте корневой контекст по выбору, но в этом случае вам придется добавить префикс корневого контекста к целевому URL.
В нашем случае мы находимся во втором сценарии, это означает, что нам придется изменить предложенный целевой URL-адрес с «http: // localhost: 9999 / simple» на «http: // localhost: 9999 / ws-proxy / simple ».
В результате мыло-пользовательский интерфейс отправляет запрос на хост / порт, указанный в настройках прокси-сервера (поэтому он отправляет запрос не на localhost: 9999, а на localhost: 8080). Путь однако сохраняется; запрос фактически отправляется на localhost: 8080 с путем «ws-proxy / simple». С модулем, развернутым в «ws-proxy», теперь вы можете видеть, почему этот префикс пути должен быть там. Если бы путь начинался с «простого», мы получили бы 404. Остальная часть пути не важна для инфраструктуры, так как сервлет диспетчера Spring (конфигурацию можно найти в WsProxyWebApplicationInitializer ) связан с «/ *». Таким образом, каждый последующий путь обрабатывается сервлетом в каждом случае.
Чтобы иметь возможность переслать сообщение фактической цели, модуль рассчитает целевой URL:
- Сначала проверьте, существует ли предварительно настроенный целевой URL-адрес для данной конечной точки на основе идентификации службы (корневой элемент полезной нагрузки + пространство имен). Это настроено в EndpointTargetUrlMapping, как мы увидим позже.
- Если ничего не найдено, проверьте, присутствует ли заголовок http Host, используйте host: port в качестве целевого сервера. Для пути используйте путь, представленный в запросе, но вычтите корневой контекст, в котором развернут этот модуль (если есть)
Последнее означает, что в нашем случае модуль развернут в «ws-proxy», а путь запроса — «ws-proxy / simple», что приведет к целевому URL-адресу «http: // localhost: 999 / simple», когда выполнив запрос, мы получим такой ответ:
В файле журнала wsproxy мы видим, что перехваченный запрос и ответ регистрируются:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
51948 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.interceptors.internalchain.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Request:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp = "http://wsproxy.error.be/" > < soapenv:Header /> < soapenv:Body > < wsp:getCurrentDate > <!--Optional:--> < arg0 >?</ arg0 > </ wsp:getCurrentDate > </ soapenv:Body > </ soapenv:Envelope > 51949 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient - Using information from Host header as hostname/port 51949 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient - Got webservice forwarding request, sending to:http://localhost:9999/simple 51981 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient - Using interceptors:[class be.error.wsproxy.interceptors.externalchain.HttpRequestHeaderTransfererInterceptor] 51981 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient$3 - Opening [org.springframework.ws.transport.http.HttpComponentsConnection@1dd5e19a] to [http://localhost:9999/simple] 51991 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient - Forwarding (http://localhost:9999/simple) done. 51994 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.interceptors.internalchain.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Response:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < S:Body > < return >2013-10-28T15:55:29.717+01:00</ return > </ ns2:getCurrentDateResponse > </ S:Body > </ S:Envelope > |
В настройках по умолчанию ведение журнала по умолчанию происходит на входящей стороне. Входящие перехватчики настраиваются здесь :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Configuration public class InboundInterceptors { @Autowired private PayloadRootAnnotationMethodEndpointMapping catchAllEndpointMapping; @Autowired private MessageDispatcher messageDispatcher; @Configuration public static class FirstInlineInterceptors { @Bean public DelegatingSmartSoapEndpointInterceptor loggingInterceptor() { return new DelegatingSmartSoapEndpointInterceptor( new LoggingInterceptor()); } } @Configuration public static class ServiceSpecificInterceptors { } @Configuration public static class LastInLineInterceptors { } } |
Если вы хотите настроить этот перехватчик журналирования также на исходящей стороне, вы можете добавить их в OutboundInterceptors . LoggingInterceptor реализует EndpointInterceptor и ClientInterceptor . Чтобы продолжить наши требования, существует также перехватчик, который может регистрировать фрагменты на основе выражения XPath. LoggingXPathInterceptor зависит от службы, поэтому мы добавим его к ServiceSpecificInterceptors. Разница заключается в том, что специфичные для службы перехватчики используют PayloadRootSmartSoapEndpointInterceptor, который нам нужен для предоставления корневого элемента пространства имен и полезной нагрузки для идентификации службы. Настроенный перехватчик будет вызываться только для этой службы. Первый в очереди и последний в очереди использования
DelegatingSmartSoapEndpointInterceptor, который будет вызываться для любого запроса.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Configuration public static class ServiceSpepcificInterceptors { @Bean public PayloadRootSmartSoapEndpointInterceptor getCurrentDateLoggingInterecptor() { LoggingXPathInterceptor loggingXPathInterceptor = new LoggingXPathInterceptor(); loggingXPathInterceptor.addRequestXPaths( new WebServiceMessageXPathExpressionMetaData( "//*[local-name()='arg0']" , "requestParameter" )); loggingXPathInterceptor.addResponseXPaths( new WebServiceMessageXPathExpressionMetaData( "//*[local-name()='return']" , "responseParameter" )); return new PayloadRootSmartSoapEndpointInterceptor(loggingXPathInterceptor, "http://wsproxy.error.be/" , "getCurrentDate" ); } } |
Когда мы снова выполняем запрос в soap-ui, мы видим, что аргумент запроса и значение ответа извлекаются и записываются в наш файл журнала:
1
2
|
DEBUG be.error.wsproxy.interceptors.internalchain.LoggingXPathInterceptor - SID:{http: //wsproxy .error.be/}getCurrentDate XPATHID:requestParameter VALUE:? DEBUG be.error.wsproxy.interceptors.internalchain.LoggingXPathInterceptor - SID:{http: //wsproxy .error.be/}getCurrentDate XPATHID:responseParameter VALUE:2013-10-28T16:50:29.537+01:00 |
WebServiceMessageXPathExpressionMetaData работает по умолчанию на теле мыла (полезная нагрузка) и обрабатывает данные XPath как обязательные (но не блокирующие). Чтобы увидеть другие варианты, проверьте Javadoc на WebServiceMessageXPathExpressionMetaData .
Свойства, которые можно настроить, находятся в пакете be.error.wsproxy.configuration.properties . Существуют следующие классы:
Стандартный профиль Spring «local», включенный через Maven по умолчанию для фильтра Maven, разрешит их из файла свойств в
wsproxy_local_demo.properties . Конфигурация всегда хранится в виде простой строки, позволяющей легко выводить ее, например, в среде JNDI. Три первых свойства определяют, как сообщения будут пересылаться, начиная с EndpointProtocolMapping:
В приведенном выше сценарии целевой URL автоматически определялся из параметров хоста, поскольку наш внутренний клиент использовал модуль в качестве прямого прокси-сервера. Поскольку параметры хоста не содержат никакого понятия о протоколах, wsproxy принял http как протокол пересылки по умолчанию. Если у вас нет обратного прокси-сервера, который заботится о разгрузке TLS, вы можете попросить прокси-модуль переадресовать через https. Вы можете сделать это, установив сопоставление протокола для https для конкретной службы:
1
|
endpoint.protocol.mapping={namespace}payloadRootElementLocalName=https,... |
EndpointTargetUrlMapping позволяет напрямую определять целевой URL. Это необходимо в случае, когда внешний клиент будет обращаться к нашему внутреннему сервису. В этом случае целевой URL больше не может быть выведен; внешние клиенты не будут использовать наш модуль в качестве прямого прокси-сервера, но сообщение просто окажется в нашем модуле, поскольку это будет фактическая служба. Затем модуль должен знать, куда он должен переслать сообщение:
1
|
endpoint.target.url.mapping={namespace}payloadRootElementLocalName=http(s)://host:port/path,.... |
Это также может быть использовано для переопределения целевого URL все вместе. Сервер пересылки сначала проверяет, существует ли явный URL-адрес, определенный для данной службы, если таковой будет присвоен приоритет.
ForwardProxy можно настроить, когда модуль wsproxy, в свою очередь, должен установить связь через http-прокси-сервер пересылки для достижения цели. Это также настраивается для каждой услуги.
Помните, что прямой прокси-сервер ничего не меняет в том, как рассчитывается целевой URL. Вместо прямого доступа к целевому URL, сообщение будет перенаправлено на настроенный прокси, если используется параметр:
1
|
forward.proxy=={namespace}payloadRootElementLocalName=host:port,... |
Хранилища ключей указывают на конфигурацию хранилища ключей, содержащую местоположение хранилища, пароль хранилища, псевдоним ключа и пароль ключа. Они используются, когда мы хотим применить защиту сообщений, о которой мы расскажем далее.
1
2
3
4
5
6
7
|
keystores.location=${project.root.dir}/config/test-keystores keystore=${keystores.location}/keystore.jks keystore.password=changeme key.alias=mykey key.password=changeme truststore=${keystores.location}/truststore.jks truststore.password=changeme |
Чтобы удовлетворить последнее требование (целостность / конфиденциальность), мы будем использовать WSS4J через Spring Wss4jSecurityInterceptor. Этот перехватчик должен быть настроен на исходящей стороне в нашем примере, где у нас есть внутренний клиент, обращающийся к внешней службе. Шаги, которые мы выполним:
- Настройка защищенной автономной конечной точки на основе мыла
- Настройте wsproxy с защитой сообщений для данной службы
- Развернуть wsproxy
- Используйте клиент веб-служб для доступа к конечной точке через прокси-модуль
Для защищенной конечной точки SimpleSecuredEndpoint предусматривается с использованием JAXWS и WSIT. Конфигурацию WSIT можно найти в META-INF / wsit-be.error.wsproxy.SimpleSecuredEndpoint $ SimpleWebServiceEndpoint.xml, обеспечивающей целостность сообщения на нашей конечной точке.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class SimpleSecuredEndpoint { public static void main(String args[]) throws IOException { // Set WSIT_HOME manually, we're only using this for testing purposes. This way we can have a dynamic path based // on the project location in filesystem to resolve the keystores via the WSIT configuratin in META-INF System.setProperty( "WSIT_HOME" , new ClassPathResource( "" ).getFile().getParent() + "/../config/test-keystores/" ); } @WebService (serviceName = "SimpleEndpoint" ) @Addressing (enabled = false , required = false ) public static class SimpleWebServiceEndpoint { public Date getCurrentDateSecured(String randomParameter) { return new Date(); } } } |
Важно: реализация JAXWS, поставляемая с JDK, не содержит WSIT. Это просто JAXWS RI. Для того, чтобы это работало, вам нужно будет загрузить последнюю версию Metro самостоятельно, которая объединяет все вместе. Смотрите домашнюю страницу метро . Когда вы загрузили Metro, запустите SimpleSecuredEndpoint, передавая одобренное системное свойство: -Djava.endorsed.dirs = / path_to_metro / lib . Это обеспечит использование всей реализации JAXWS из внешних библиотек. Когда все работает нормально, вы увидите строку:
1
|
INFO: WSP5018: Loaded WSIT configuration from file : file : /home/koen/ .... |
,
Конфигурация перехватчика WSS4J, обеспечивающего целостность сообщения в OutboundInterceptors :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@Bean public Map<QName, List<ClientInterceptor>> customClientInterceptors() throws Exception { Map<QName, List<ClientInterceptor>> mapping = new HashMap<>(); List<ClientInterceptor> list = new ArrayList<>(); list.add(getCurrentDateServiceSecurityInterceptor()); list.add( new LoggingInterceptor()); return mapping; } private Wss4jSecurityInterceptor getCurrentDateServiceSecurityInterceptor() throws Exception { Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor(); // Outgoing interceptor.setSecurementActions( "Signature Timestamp" ); interceptor .setSecurementSignatureParts( "{}{http://schemas.xmlsoap.org/soap/envelope/}Body;{}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp" ); interceptor.setSecurementSignatureKeyIdentifier( "IssuerSerial" ); Pair<String, String> key = keystore.getKeyAliasPasswords().get( 0 ); interceptor.setSecurementUsername(key.getLeft()); interceptor.setSecurementPassword(key.getRight()); interceptor.setSecurementTimeToLive( 700 ); interceptor.setValidationTimeToLive( 700 ); interceptor.setSecurementSignatureCrypto(keystoreCrypto); // Incomming interceptor.setValidationActions( "Timestamp Signature" ); interceptor.setValidationSignatureCrypto(truststoreCrypto); return interceptor; } |
В строках 6 и 7 мы добавляем пользовательский перехватчик в список перехватчиков, используемых ForwardingClient. Мы также добавили LoggingInterceptor на исходящие, чтобы мы могли видеть, как защищенные сообщения выходят и входят. Чтобы проверить конфигурацию безопасности сообщений, разверните wsproxy и используйте soap-ui для запуска запроса. Настройка soap-ui ничем не отличается от настройки для незащищенной конечной точки.
Важно: Кажется, есть проблема с C14N. Когда запрос отправляется в обычном режиме, WSIT будет жаловаться на то, что рассчитанный дайджест не совпадает с дайджестом в сообщении. Я собираюсь исследовать это дальше, но это, похоже, проблема WSIT, а не WSS4J, поскольку та же проблема возникает и тогда, когда soap-ui настроен как защищенный клиент и напрямую связывается с конечной точкой, а не с помощью модуля wsproxy. , Чтобы обойти это и увидеть, как работает тест, удалите перевод строки между стартовым элементом soap Body и корневым стартовым элементом payload. Также удалите перевод строки между конечным элементом мыльного тела и корневым конечным элементом полезной нагрузки:
1
2
3
4
5
6
7
|
< soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp = "http://wsproxy.error.be/" > < soapenv:Header /> < soapenv:Body >< wsp:getCurrentDateSecured > <!--Optional:--> < arg0 >?</ arg0 > </ wsp:getCurrentDateSecured ></ soapenv:Body > </ soapenv:Envelope > |
Проекты soap-ui также доступны в проекте, если хотите. Не забудьте включить настройки прокси, как описано выше, они не сохраняются как часть проекта.
Результат:
1
2
3
4
5
6
7
8
|
< S:Envelope xmlns:S = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds = "http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n = "http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs = "http://www.w3.org/2001/XMLSchema" > < S:Header /> < S:Body wsu:Id = "_5002" > < return >2013-10-29T14:26:25.789+01:00</ return > </ ns2:getCurrentDateSecuredResponse > </ S:Body > </ S:Envelope > |
Ничего особенного, так как wsproxy добавил безопасность сообщений при пересылке запроса и удалил его при возврате ответа. Если мы посмотрим на файлы журнала wsproxy, то сначала увидим запрос, поступающий со стороны inboud:
01
02
03
04
05
06
07
08
09
10
|
34 [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Request:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp = "http://wsproxy.error.be/" > < soapenv:Header /> < soapenv:Body > < wsp:getCurrentDateSecured > <!--Optional:--> < arg0 >?</ arg0 > </ wsp:getCurrentDateSecured > </ soapenv:Body > </ soapenv:Envelope > |
Запрос защищен и направлен в конечную точку:
01
02
03
04
05
06
07
08
09
10
11
|
394 [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate OUTBOUND SIDE Request:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp = "http://wsproxy.error.be/" > < soapenv:Header > < wsse:Security xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:mustUnderstand = "1" > < wsu:Timestamp wsu:Id = "TS-518848887F924441AB13830540361321" > < wsu:Created >2013-10-29T13:40:36.130Z</ wsu:Created > < wsu:Expires >2013-10-29T13:45:36.130Z</ wsu:Expires > </ wsu:Timestamp > < ds:Signature xmlns:ds = "http://www.w3.org/2000/09/xmldsig#" Id = "SIG-518848887F924441AB13830540361916" > < ds:SignedInfo > ... |
Защищенный ответ получен от конечной точки;
01
02
03
04
05
06
07
08
09
10
11
|
524 [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate OUTBOUND SIDE Response:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < S:Envelope xmlns:S = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds = "http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n = "http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs = "http://www.w3.org/2001/XMLSchema" > < S:Header > < wsse:Security S:mustUnderstand = "1" > < wsu:Timestamp xmlns:ns15 = "http://www.w3.org/2003/05/soap-envelope" xmlns:ns16 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" wsu:Id = "_3" > < wsu:Created >2013-10-29T13:40:36Z</ wsu:Created > < wsu:Expires >2013-10-29T13:45:36Z</ wsu:Expires > </ wsu:Timestamp > < ds:Signature xmlns:ns15 = "http://www.w3.org/2003/05/soap-envelope" xmlns:ns16 = "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" Id = "_1" > < ds:SignedInfo > ... |
Информация о безопасности обрабатывается и проверяется. Если все в порядке, информация о безопасности удаляется и возвращается ответ (нашему клиенту, в данном случае мыло-пользовательский интерфейс):
1
2
3
4
5
6
7
8
9
|
567 [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Response:<? xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> < S:Envelope xmlns:S = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds = "http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n = "http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs = "http://www.w3.org/2001/XMLSchema" > < S:Header /> < S:Body wsu:Id = "_5002" > < return >2013-10-29T14:40:36.357+01:00</ return > </ ns2:getCurrentDateSecuredResponse > </ S:Body > </ S:Envelope > |
До сих пор все тесты выполнялись, имитируя внутренний клиент, обращающийся к внешней службе. В случае, если вы хотите использовать модуль, чтобы сделать обратное; обслуживая внешних клиентов, обращающихся к внутренним службам, это то же самое. Для каждой внутренней службы, которую будет перенаправлять модуль, необходимо зарегистрировать целевой URL-адрес с помощью параметра конфигурации endpoint.target.url.mapping . Перехватчики продолжают работать таким же образом, но помните, что, например, для безопасности сообщений вы, вероятно, хотите, чтобы Wss4jSecurityInterceptor был настроен на входящей стороне, поскольку в этом сценарии входящая сторона является внешней обращенной стороной. Нет проблем в настройке Wss4jSecurityInterceptor на входящей и исходящей сторонах для другой службы; вся конфигурация основана на основе обслуживания.
Например: служба x (идентифицируемая корневым элементом пространства имен и полезной нагрузки) является внутренней размещенной службой. Сервис y — это внешний сервис, к которому хотят обращаться внутренние клиенты. Для защиты нашей внутренней службы x мы добавим Wss4jSecurityInterceptor в качестве входящего перехватчика для конкретной службы в конфигурации InboundInterceptors . Таким образом, этот перехватчик будет активен только в конечной точке wsproxy (обслуживающей только входящую сторону, в данном примере внешнюю обращенную сторону) и только для службы x. Для защиты обращений к сервису y мы зарегистрировали бы Wss4jSecurityInterceptor в OutboundInterceptors , добавив безопасность сообщений для сообщений, отправляемых внешней службе y модулем wsproxy.
Хорошо, вот и все! Не стесняйтесь, напишите мне, если это было как-то полезно или если у вас есть идеи по улучшению!