SOAP клиент с авторизацией

Описание и примеры использования SOAP клиентов открытых web-сервисов широко представлены как в интернете, так и на нашем сайте. Но иногда разработчикам приходится сталкиваться с закрытыми web-сервисами SOAP, требующих авторизации. Поскольку это не тривиальная задача, и, если учесть, что приходится заниматься ей от случая к случаю, то было решено также и на нашем сайте оставить следы её реализации.

Данная статья является продолжением описания SOAP клиента, представленного на сайте, с тем отличием, что здесь будет рассмотрен аспект авторизованного подключения к WEB-сервису.

Попробуем представить постановку задачи, поступившей от «серьезного» Заказчика (реальная) :
необходимо получать определенную информацию в формате XML, анализировать ее и складывать в базу данных для дальнейшей обработки. В качестве источника можно использовать либо JMS-сообщения из очередей приложения IBM WebSphere MQ, либо от двух SOAP-сервисов. Заказчик предпочтение отдает 2-му варианту.

Поскольку уже для Заказчика создано несколько приложений с использованием очередей MQ, то не стали упираться и решили расширить область используемых технологий, разрабатывая SOAP клиента с авторизацией. Сначала необходимо набросать небольшой код для определения требуемых параметров SOAP клиента, которые должен предоставить Заказчик :

import javax.xml.soap.*;

// URL WEB-сервисов
private String urlSummer = "http://99.99.99.99:999/service/summer";
private String urlWinter = "http://99.99.99.99:999/service/winter";

private String namespace       = "...";
private String targetNamespace = "...";

// Параметры авторизации
private String login           = "guest";
private String password        = "service";

// Наименования WEB-сервисов
private String svcSummer       = "summer";
private String svcWinter       = "winter";
//-----------------------------------------
// Производные параметры WEB-сервиса
private String soapUrl         = null;
private String serviceName     = null;
private String soapAction      = null;

/*
 * Флаг использования одного из сервисов :
 * если true, то сервис summer, в противном случае winter
 */
private boolean  SERVICE_summer = true;

//-------------------------------------------------------------------
private void setSoapParams() {
    soapUrl     = SERVICE_xxx ? urlSummer : urlWinter;
    serviceName = SERVICE_xxx ? svcSummer : svcWinter;

    soapAction  = targetNamespace + "/" + serviceName;
}

Итак, для использования 2-х закрытых WEB-сервисов необходим их URL (urlSummer, urlWinter), наименование namespace, targetNamespace и параметры авторизации. На этапе отладки в процедуре setSoapParams определяются параметры подключения к одному определенному web-сервису. Получили от Заказчика всё за исключением namspace и targetNamespace; наименование web-сервисов извлекли из URL. Чтобы получить отсутствующие два параметра дополнительно запросили WSDL сервисов, в которых их можно найти :

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
	xmlns:xxx="http://xxxxx-xx.com/xxx" 
	xmlns:xs="http://www.w3.org/2001/XMLSchema" 
	xmlns="http://schemas.xmlsoap.org/wsdl/" 
	targetNamespace="http://xxxxx-xx.com/xxx" 
	name="summer">
	
	<!-- SOAP 1.1 WSDL - dynamically generated by XXX. -->
	
	<types>
	. . .
	</types>
	
	<message name="exportRequestMsg">
	. . .
	</message>
	<message name="exportResponseMsg">
	. . .
	</message>
	<message name="exportExceptionMsg">
		<part name="fault" element="xxx:AIMSoapExportException"/>
	</message>
	
	<portType name="summerImpl">
	. . .
	</portType>
	
	<binding name="summerPortBinding" type="xxx:summerImpl">
	. . .
	</binding>
	
	<service name="summer">
	. . .
	</service>
</definitions>

Наименование namespace (xmlns:xxx) и targetNamespace ( http://xxxxx-xx.com/xxx ) включены в качестве атрибутов в тег описания сервиса <definitions>. Подставляем их значения в переменные определения (код в начале). Ну а остальное дело техники. Пишем процедуры формирования SOAP сообщения, SOAP запроса и обращения к WEB-сервису.

Процедура формирования тела SOAPMessage

Процедура createSoapEnvelope в качестве параметра получает значение типа SOAPMessage. В теле процедуры определяются namespace, targetNamespace (метод addNamespaceDeclaration) и формируется тело сообщения soapBody, которое может быть намного сложнее, чем это представлено в коде, и включать определенные параметры запроса к SOAP-сервису.

private void createSoapEnvelope(SOAPMessage soapMessage) 
                                   throws SOAPException {
    SOAPPart soapPart = soapMessage.getSOAPPart();

    // SOAP Envelope
    SOAPEnvelope envelope = soapPart.getEnvelope();
    envelope.addNamespaceDeclaration(namespace, targetNamespace);

    // SOAP Body
    SOAPBody soapBody = envelope.getBody();
    soapBody.addChildElement(serviceName, namespace);
}

Процедура создания SOAPMessage с авторизацией

В процедуре createSOAPRequest создается объект SOAPMessage. В заголовочную часть SOAP сообщения вставляются описания SOAPAction и кодированное значение параметров авторизации 'auth' в формате «login:password». Для кодирования строки «login:password» используется кодировщик утилиты java.util.Base64. В заголовок SOAP сообщения параметры авторизации добавляются в кодированном виде с ключом «Authorization»; кодированное значение 'auth' добавляется с префиксом "Basic ", от которого оно отделено символом пробела.

Текст сформированного SOAP сообщения выводится в консоль (без отображения заголовка).

private SOAPMessage createSOAPRequest(String soapAction) 
                                       throws Exception {
    MessageFactory messageFactory = MessageFactory.newInstance();
    SOAPMessage    soapMessage    = messageFactory.createMessage();

    createSoapEnvelope(soapMessage);

    MimeHeaders headers = soapMessage.getMimeHeaders();
    headers.addHeader("SOAPAction", soapAction);

    // Определение авторизации сервиса
    String loginPassword = login + ":" + password;
    byte[]  bytes = loginPassword.getBytes();
    String auth = new String(Base64.getMimeEncoder().encode(bytes));
    headers.addHeader("Authorization", "Basic " + auth);

    soapMessage.saveChanges();

    // Печать XML текста запроса в консоль
    soapMessage.writeTo(System.out);
    System.out.println("\n");

    return soapMessage;
}

Процедура вызова сервиса

Процедура callSoapWebService включает вызовы всех вышеописанных процедур для формирования и оправки SOAP запроса и получения от WEB-сервиса ответа. Сначала определяются параметры WEB-сервиса, после этого формируется запрос soapRequest. Для подключения к WEB-сервису создается SOAPConnection, который в качестве параметров получает запрос soapRequest и soapUrl. Объект подключения soapConnect возвращает от сервиса ответ soapResponse типа SOAPMessage, который, в зависимости от формата, можно ковертировать либо в XML, либо в JSON. В процедуре parseSOAPResponse ответ сервиса soapResponse переводится в текстовый вид.

private void callSoapWebService()
{
    SOAPConnectionFactory soapFactory  = null;
    SOAPConnection        soapConnect  = null;
    SOAPMessage           soapRequest  = null;
    SOAPMessage           soapResponse = null;
    try {
        // Определение параметров сервиса
        setSoapParams();

        // Создание SOAP Message для отправки
        soapRequest  = createSOAPRequest(soapAction);

        // Создание SOAP Connection
        soapFactory = SOAPConnectionFactory.newInstance();
        soapConnect = soapFactory.createConnection();

        // Получение SOAP Message
        soapResponse = soapConnect.call(soapRequest, soapUrl);

        // Разложение SOAP Response
        parseSOAPResponse (soapResponse);

        soapConnect.close();
    } catch (Exception e) {
        System.err.println("Exception : " + e.getMessage());
    }
}

Процедура обработки ответа

Ответ WEB-сервиса типа SOAPMessage конвертируется в процедуре parseSOAPResponse сначала в текстовый вид с использованием потока ByteArrayOutputStream. Далее, в зависимости от формата ответа и цели обращения к сервису, ответ может быть преобразован в XML, JSON и т.д.

private void parseSOAPResponse (SOAPMessage soapResponse) {
    try {
        // Получение содержимого ответа
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        soapResponse.writeTo(stream);
        String content = new String(stream.toByteArray(), "utf-8"); 
        System.out.println(content);
        // ...			
    } catch (Exception e) {
        System.err.println("Exception : " + e.getMessage());
    }
}

Листинг примера

Представленные выше листинги процедур были извлечены из примера SoapClientExample. Вы можете их использовать по собственному усмотрению с внесением необходимых доработок. Полагаю Вам не составит особого труда весь описанный выше код в виде полей и процедур вставить в класс SoapClientExample, чтобы получить завершенный пример.

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Base64;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.*;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class SoapClientExample 
{
    . . .
    //---------------------------------------------------------------
    public SoapClientExample()
    {
        callSoapWebService();
    }
    //---------------------------------------------------------------
    public static void main(String[] args) 
    {
        new SoapClientExample();
        System.exit(0);
    }
}
  Рейтинг@Mail.ru