Мне нужно использовать веб-службу SOAP 1.2, которая использует WS-Security. В работе я использую Delphi 10.2 Tokyo и .Net Core. Ни один из них, похоже, не работает.
При попытке использовать встроенный в Delphi 10.2 Tokyo импортер WSDL (с использованием SOAP 1.2 с Indy) мои запросы отправляются без какой-либо информации заголовка SOAP. Этот недопустимый запрос вызывает ошибку Сброс соединения пиром.
При попытке с .Net Core (с использованием поставщика ссылок на веб-службы WCF) я получаю сообщение об ошибке: System.PlatformNotSupportedException: «TransportSecurityBindingElement.BuildChannelFactoryCore не поддерживается».
Предположительно из-за того, что требуемая функция еще не поддерживается .Net Core: https://github.com/dotnet/wcf/blob/master/release-notes/SupportedFeatures-v2.0.0.md (Security.Message не поддерживается)
Ниже приведен WSDL. Я удалил ненужный XML из этого файла и заменил фактическое название компании на Example. Надеюсь, это даст достаточное представление о политиках/требованиях WSDL.
<?xml version = "1.0" encoding = "UTF-8"?>
<wsdl:definitions
xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"
xmlns:i0 = "http://tempuri.org/"
xmlns:msc = "http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:soapenc = "http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns = "www.example.com/ExampleService"
xmlns:wsa = "http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsa10 = "http://www.w3.org/2005/08/addressing"
xmlns:wsam = "http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsap = "http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
xmlns:wsaw = "http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsp = "http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsx = "http://schemas.xmlsoap.org/ws/2004/09/mex"
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
name = "ExampleService"
targetNamespace = "www.example.com/ExampleService">
<wsp:Policy wsu:Id = "ExampleAPI_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate = "false" />
</wsp:Policy>
</sp:TransportToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
<sp:EndorsingSupportingTokens xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:SecureConversationToken sp:IncludeToken = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:BootstrapPolicy>
<wsp:Policy>
<sp:SignedParts>
<sp:Body />
<sp:Header Name = "To" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "From" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "FaultTo" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "ReplyTo" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "MessageID" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "RelatesTo" Namespace = "http://www.w3.org/2005/08/addressing" />
<sp:Header Name = "Action" Namespace = "http://www.w3.org/2005/08/addressing" />
</sp:SignedParts>
<sp:EncryptedParts>
<sp:Body />
</sp:EncryptedParts>
<sp:TransportBinding>
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate = "false" />
</wsp:Policy>
</sp:TransportToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10 />
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss11>
<wsp:Policy />
</sp:Wss11>
<sp:Trust10>
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust10>
</wsp:Policy>
</sp:BootstrapPolicy>
</wsp:Policy>
</sp:SecureConversationToken>
</wsp:Policy>
</sp:EndorsingSupportingTokens>
<sp:Wss11 xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy />
</sp:Wss11>
<sp:Trust10 xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust10>
<wsaw:UsingAddressing />
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id = "ExampleAPI2_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate = "false" />
</wsp:Policy>
</sp:TransportToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10 />
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss10 xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy />
</sp:Wss10>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsdl:types>
<!-- trimmed away types -->
</wsdl:types>
<!-- trimmed away messages -->
<wsdl:portType name = "IAPIService">
<!-- trimmed away operations -->
</wsdl:portType>
<wsdl:binding name = "ExampleAPI" type = "tns:IAPIService">
<wsp:PolicyReference URI = "#ExampleAPI_policy" />
<soap12:binding transport = "http://schemas.xmlsoap.org/soap/http" />
<!-- trimmed away operations -->
</wsdl:binding>
<wsdl:binding name = "ExampleAPI2" type = "tns:IAPIService">
<wsp:PolicyReference URI = "#ExampleAPI2_policy" />
<soap:binding transport = "http://schemas.xmlsoap.org/soap/http" />
<!-- trimmed away operations -->
</wsdl:binding>
<wsdl:service name = "ExampleService">
<wsdl:port name = "ExampleAPI" binding = "tns:ExampleAPI">
<soap12:address location = "https://example.com/apps/api/APIService.svc" />
<wsa10:EndpointReference>
<wsa10:Address>https://example.com/apps/api/APIService.svc</wsa10:Address>
</wsa10:EndpointReference>
</wsdl:port>
<wsdl:port name = "ExampleAPI2" binding = "tns:ExampleAPI2">
<soap:address location = "https://example.com/apps/api/APIService.svc/endpoint2" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>





Не существует конкретной «лучшей» платформы для использования веб-сервиса. Однако некоторые платформы лучше других поддерживают автоматическую генерацию прокси-классов.
When trying with Delphi 10.2 Tokyo's built in WSDL importer (using SOAP 1.2 with Indy), my requests are sent without any SOAP header information.
У меня нет опыта работы с .NET Core, но при использовании Delphi 10.2 вам необходимо вручную добавить (отправить) заголовок SOAP перед отправкой запроса.
Это можно сделать, приведя экземпляр веб-сервиса к ISOAPHeaders
Пример:
procedure TestSendHeader;
var
MyService: IMySoapWebservice;
header : TMySoapHeader;
begin
MyService := GetMySoapService();
(MyService as ISOAPHeaders).OwnsSentHeaders := True;
// Create header object
header := TMySoapHeader.Create;
// Fill header data
// header.xyz := ;
// 'Send' header with the request
(MyService as ISOAPHeaders).Send(header);
MyResponse := MyService.DoMyRequest();
end;
Установка OwnsSentHeaders на true освободит созданный объект заголовка после отправки запроса.
ПРИМЕЧАНИЕ: вы должны «Отправить» заголовок каждый раз, когда делаете запрос:
// 'Send' header with the request 1
(MyService as ISOAPHeaders).Send(header);
MyResponse := MyService.DoMyRequest1();
// 'Send' header with the request 2
(MyService as ISOAPHeaders).Send(header);
MyResponse := MyService.DoMyRequest2();
В этом случае не устанавливайте OwnsSentHeaders на true и освобождайте объект заголовка после того, как вы закончите отправку запросов.
У меня есть некоторый код, относящийся к базовой структуре WS-безопасности (xml-объект), который я посмотрю завтра. Но шифрование данных (кодировка base64) не является частью этого, поэтому вы должны сделать это самостоятельно.
Я разместил код Delphi, касающийся заголовка WS-Security. Обратите внимание, что есть две версии (по крайней мере, о которых я знаю) - 2003.06 и 2004.01. Оба добавлены в суть: gist.github.com/ronaldhoek/ff7611a0b4bd03c01f72c3dd5a4f0a5c
Что касается кодирования, в Delphi есть модуль под названием «System.NetEncoding» (начните с Delphi 10 или около того), который поможет в кодировании/декодировании данных.
Спасибо большое. Я посмотрю на WS-безопасность в Delphi, когда у меня будет свободное время. Выглядит многообещающе. Тем временем я решил заняться разработкой .NET Framework DLL, которую может использовать Delphi.
.NET Framework полностью поддерживает службу SOAP. Я разработал приложение .NET Framework на C# и создал библиотеку DLL, которую можно использовать в Delphi, .NET Core и т. д. Это немного окольное решение, но самое простое, которое я смог найти.
Доступ к службам на C#: https://docs.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
Экспорт функций DLL: Можно ли экспортировать функции из C# DLL, как в VS C++?
Означает ли это, что мне придется самостоятельно реализовывать функции WS-Security и WS-Addressing? Кажется, что это огромная задача, поскольку она включает в себя шифрование, подпись данных и т. д. Может быть, кто-то уже проделал тяжелую работу и выложил ее на GitHub? Хотя ничего не нашел...