Правильный способ передачи токена имени пользователя WSSE для веб-службы SOAP

Я пытаюсь использовать веб-службу через соответствующий wsdl. Эта служба зависит от аутентификации, соответствующей базовому профилю безопасности 1.0 для безопасности веб-служб включая правильное пространство имен xmls http://docs.oasis-open.org/wss/2004/01/oasis-200401wss-wssecurity-secext-1.0.xsd необходимо включить в запрос.

Пример:

<wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >
   <wsse:Username>
      Bob
   </wsse:Username>
   <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>
      1234
   </wsse:Password>
</wsse:UsernameToken>

Мои первые попытки были связаны с Add Service Reference нацеливанием на wsdl и сгенерированными прокси, использующими их как таковые.

ServicePointManager.ServerCertificateValidationCallback = 
    (object s, X509Certificate certificate, X509Chain chain,
                     SslPolicyErrors sslPolicyErrors) => true;

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
basicHttpBinding.Security.Transport.ClientCredentialType = 
                                                HttpClientCredentialType.Basic;

var endpoint = new EndpointAddress("https://secure-ausomxana.crmondemand.com/..."

using (var client = new ContactClient(basicHttpBinding, endpoint))
{

    var credential = client.ClientCredentials.UserName;
    credential.UserName = "bob";
    credential.Password = "1234";

    var input = ...    
    var output = client.ContactQueryPage(input);
}

Однако, пытаясь опросить сообщения SOAP с помощью Fiddler, я вижу, что элемент UsernameToken не был добавлен.

Как правильно выполнять этот контракт?

Изменить: после ответа @John Saunders я попытался изменить свой код, чтобы использовать wsHttpBinding.

var wsHttpBinding = new WSHttpBinding(SecurityMode.Transport);
wsHttpBinding.Security.Transport.ClientCredentialType =
                                         HttpClientCredentialType.Basic;

Используя эту привязку, сообщение SOAP становится

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">document/urn:crmondemand/ws/ecbs/contact/10/2004:ContactQueryPage</a:Action>
    <a:MessageID>urn:uuid:17807f44-1fcasfdsfd</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">
      <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">
        <Contact>
          <Id>1-asdfd</Id>
        </Contact>
      </ListOfContact>
    </ContactQueryPage_Input>
  </s:Body>
</s:Envelope>

Это добавляет элемент Header, в отличие от элемента wsse:UsernameToken для ссылки на исходное сообщение мыла с использованием BasicHttpBinding.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">
      <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">
        <Contact>
          <Id>1-asdfds</Id>
        </Contact>
      </ListOfContact>
    </ContactQueryPage_Input>
  </s:Body>
</s:Envelope>

Если я изменю привязку на

var wsHttpBinding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

Сообщение SOAP, которое я получаю,

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</a:Action>
    <a:MessageID>urn:uuid:eeb75457-f29e-4c65-b4bf-b580da26e0c5</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>
    <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
      <u:Timestamp u:Id="_0">
        <u:Created>2011-05-02T13:30:09.360Z</u:Created>
        <u:Expires>2011-05-02T13:35:09.360Z</u:Expires>
      </u:Timestamp>
      <o:UsernameToken u:Id="uuid-dc3605a0-6878-42f4-b1f2-37d5c04ed7b4-2">
        <o:Username>Bob</o:Username>
        <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
      <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      <t:Entropy>
        <t:BinarySecret u:Id="uuid-7195ad74-580b-4e52-9e2c-682e5a684345-1" Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce">bI4xuyKwZ8OkQYBRnz2LDNV+zhIOnl0nwP24yI1QAwA=</t:BinarySecret>
      </t:Entropy>
      <t:KeySize>256</t:KeySize>
    </t:RequestSecurityToken>
  </s:Body>
</s:Envelope>

Кажется, это очень близко, но похоже, что на самом деле зашифровано тело мыльного сообщения, чего я НЕ хочу.

Если я укажу wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; с использованием только SecurityMode.Transport, он вернется туда, где указано его анонимность.

Какое последнее препятствие я не могу преодолеть?

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

<system.serviceModel>
  <bindings>    
    <basicHttpBinding>
      <binding name="Contact" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
          bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="524288" maxBufferPoolSize="524288" maxReceivedMessageSize="524288"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
          useDefaultWebProxy="true">
        <readerQuotas maxDepth="32" maxStringContentLength="65536" maxArrayLength="131072"
            maxBytesPerRead="32768" maxNameTableCharCount="131072" />
        <security mode="Transport">
          <transport clientCredentialType="None" proxyCredentialType="None"
              realm="" />
          <message clientCredentialType="UserName" algorithmSuite="Default" />
        </security>
      </binding>         
    </basicHttpBinding>
  </bindings>
  <client>
    <endpoint address="https://secure-ausomxana.crmondemand.com/Services/Integration"
       binding="basicHttpBinding" bindingConfiguration="Contact"
       contract="OnDemandContactService.Contact" name="OnDemand.Contact.Endpoint">
      <headers>        
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
          <wsse:UsernameToken>
            <wsse:Username>USERNAME</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">PASSWORD</wsse:Password>
          </wsse:UsernameToken>
        </wsse:Security>
      </headers>
    </endpoint>
  </client>
</system.serviceModel>

См. С C #, потребителем WCF SOAP, который использует аутентификацию в виде обычного текста WSSE?, чтобы узнать, как настроить ее с помощью кода, а не конфигурации


person Chris Marisic    schedule 29.04.2011    source источник
comment
Почему вы устанавливаете базовую транспортную безопасность? Вам нужна как транспортная, так и аутентификация сообщений?   -  person Ladislav Mrnka    schedule 02.05.2011
comment
Как вы думаете, можете ли вы отредактировать это, чтобы показать краткий рецепт? Это сложно проделать через повествование; Я не совсем понимаю, что вы добавляете и вычитаете по мере продолжения повествования.   -  person Aren Cambre    schedule 15.01.2013
comment
@ArenCambre Краткая версия - если вы ожидаете, что сообщение будет использовать wsse:Security, вам нужно использовать статическое присваивание в web.config, как в моем окончательном решении. Если вам нужно изменить имя пользователя и пароль, насколько я могу судить, вы SOL.   -  person Chris Marisic    schedule 16.01.2013
comment
Спасибо. В итоге я разместил свой вопрос, и с помощью других мы нашли способ сделать это: stackoverflow.com/questions/14327960/   -  person Aren Cambre    schedule 16.01.2013
comment
то, как вы включаете безопасность wsse в заголовок конфигурации, потрясающе, и я пытался реализовать его в коде, который у меня занял целую вечность.   -  person Praneeth    schedule 10.10.2013
comment
@Praneeth да, я отказался от попыток реализовать это в коде, у меня ничего не получилось, но с использованием этого блока это было гладко для аутентификации на ужасном мыльном API, с которым мне пришлось иметь дело.   -  person Chris Marisic    schedule 11.10.2013
comment
этот вопрос мне действительно помог. спасибо за вопрос :)   -  person cahit beyaz    schedule 19.05.2015
comment
Были ли ваше имя пользователя и пароль жестко прописаны в конфигурации или вы как-то обновляли их во время выполнения? Тем не менее, отличный пост, очень полезный.   -  person DomBat    schedule 06.10.2016
comment
@DomBat Я использовал его только прямо в конфигурации, ссылка внизу моего вопроса может позволить вам изменить учетные данные во время выполнения.   -  person Chris Marisic    schedule 06.10.2016
comment
Старый пост, но у нас здесь все еще есть люди, использующие SOAP ... :( Заголовки клиентов помогли мне решить мою проблему. Мне не понадобились дополнительные настройки транспорта и сообщений в привязке. Спасибо   -  person Jim    schedule 26.06.2018


Ответы (3)


Если вам нужно отправить UserName через HTTPS, вы можете использовать стандартный подход (если ваш WSDL определен правильно, он должен быть создан для вас автоматически путем добавления ссылки на сервис):

<bindings>
  <basicHttpBinding>
    <binding name="secured">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="UserName" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint name="..." address="https://..." contract="..." binding="basicHttpBinding"
            bindingConfiguration="secured" />
</client>

Ar вы можете определить привязку в коде:

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
basicHttpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

Вы установите учетные данные в прокси, как вы это делаете сейчас:

client.ClientCredentials.UserName.UserName = "bob";
client.ClientCredentials.UserName.Password = "1234";

Если вам нужен только профиль UserNameToken через HTTP без какой-либо другой инфраструктуры WS-Security, самый простой подход - использовать ClearUserNameBinding.

Если вам нужны одно и то же имя пользователя и пароль для всех запросов от клиента, вы можете использовать простую привязку basicHttpBinding без какой-либо защиты и включить статический заголовок из конфигурации:

<client>
  <endpoint ...>
    <headers>
      <wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >
        <wsse:Username>Bob</wsse:Username>
        <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>
           1234
        </wsse:Password>
      </wsse:UsernameToken>
    </headers>
  </endpoint>
</client> 

Если вам нужно что-то более сложное, покажите соответствующую часть WSDL (утверждение безопасности) или образец запроса SOAP. Также укажите, требуется ли вам использовать HTTP или HTTPS.

person Ladislav Mrnka    schedule 02.05.2011
comment
Да, этот заголовок будет использоваться в каждом запросе без изменения какой-либо информации, кроме требования отправки UsernameToken, единственная другая спецификация - доступ к службе через HTTPS. - person Chris Marisic; 02.05.2011
comment
@Chris: Я добавил некоторую конфигурацию для HTTPS - person Ladislav Mrnka; 02.05.2011
comment
Ладислав, это определенно кажется решением, а точнее просто вставкой строки заголовка в определение конечной точки. Я ценю вашу помощь, поскольку такая конфигурация не является моей сильной стороной. Я последую за наградой за награду после разговора с поставщиком услуг, и мне кажется, что теперь я могу правильно с ними общаться. Тем не менее, что их служба сейчас просто заблокирована, а не мои мыльные сообщения. - person Chris Marisic; 02.05.2011
comment
Это определенно было решением, я так и не смог найти где-либо, где установить имя пользователя и пароль с помощью кода, но использование элемента заголовков в конфигурации сделало это. - person Chris Marisic; 03.05.2011
comment
Если вы получаете MessageSecurityException, посмотрите мой ответ ниже. Возможно, вам потребуется удалить отметку времени и также установить тип содержимого. - person Ionian316; 30.04.2015

@ Ладислав ответ правильный. Однако я получал MessageSecurityException для веб-службы SOAP 1.1, которую пытался использовать. После этого сообщения в блоге Скотта Хансельмана я смог заставить его работать. Вот код, который я использую:

    var oldBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
    oldBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
    //remove the timestamp
    BindingElementCollection elements = oldBinding.CreateBindingElements();
    elements.Find<SecurityBindingElement>().IncludeTimestamp = false;
    //sets the content type to application/soap+xml
    elements.Find<TextMessageEncodingBindingElement>().MessageVersion = MessageVersion.Soap11;
    CustomBinding newBinding = new CustomBinding(elements);
person Ionian316    schedule 30.04.2015
comment
Читая этот пост в блоге, вы уверены, что он действительно отвечает на мой первоначальный вопрос? При чтении сообщения выясняется, что MessageCredentialType.UserName отправит только имя пользователя, что было бы полезно для аутентификации в стиле ApiKey, нарушая стандарты WS-Security. Я никогда не понимал, почему люди используют стандарты, которые мгновенно их нарушают. Я полагаю, что стандарты WS- * настолько абсурдно загадочны, что никто не может разумно понять их, чтобы следовать им, даже если они имеют в виду то же самое. Стандарты WS- * отбрасывают SOA на полдесятилетия назад. Микросервисы намного ближе к цели SOA - person Chris Marisic; 30.04.2015
comment
Фактически, ClientCredentialType UserName включает свойства UserName и Password. Ответ @ ladislav помог мне встать на правильный путь. Мне просто нужно было добавить еще несколько изменений, чтобы он работал для веб-службы, которую я вызываю. Я разместил это как ответ, потому что 1) это может помочь другим, имеющим такую ​​же проблему, и 2) я знаю, что это должен быть комментарий под его ответом, но код в комментарии плохо форматируется. Я согласен с тем, что стандарты WS- * слишком сложны. - person Ionian316; 01.05.2015

Используйте wsHttpBinding, а не basicHttpBinding.

Фактически, вы должны просто использовать «Добавить ссылку на службу» и указать на WSDL службы.

person John Saunders    schedule 29.04.2011
comment
Я не контролирую конечную точку, и конечная точка, вероятно, нарушает некоторые серии бесконечных перестановок абсурдных стандартов WSSE. - person Chris Marisic; 19.05.2015
comment
Вау, использование WSHttpBinding вместо BasicHttpBinding отправляет множество дополнительных заголовков (WS-Addressing, Action, Trust и т. Д.) В дополнение к Security. Моя просьба увеличилась вдвое. Нет, спасибо. Но добавить ссылку на службу - хороший совет. - person rustyx; 23.08.2018
comment
@rustyx использует ли ваш сервис безопасность WSSE? Это вопрос о WSSE. - person John Saunders; 23.08.2018
comment
Да, он использует защиту паролем WSSE (только). - person rustyx; 23.08.2018
comment
@rustyx, тогда я не понимаю, как это работает с basicHttpBinding. - person John Saunders; 23.08.2018
comment
Каким-то образом настройка ClientCredentialType = BasicHttpMessageCredentialType.UserName отправляет правильный заголовок WS-Security. - person rustyx; 23.08.2018