Вопрос по wcf, c#, web-services – (Попытка) выполнить миграцию с WSE 3.0 на WCF для клиентского кода

10

Я был по всей сети для этого. Я только что делал это, и поставщик, чей веб-сервис я пытаюсь использовать, отказывается официально поддерживать WCF как метод потребления.

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

The service

В моей компании мы используем приложение поставщика, которое предоставляет сервис. Приложение написано на Java, и похоже, что wsdl был создан с Apache Axis 1.2.

The code

Мой старый код использует WSE 3.0. В частности, он использует прокси-классы, имеющие «WSE». авто-прикрепленный в конце. Это позволяет мне использовать гораздо более простую схему аутентификации (единственный способ заставить ее работать). Мне не нужно использовать сертификаты. Я использую производную отSecurityPolicyAssertionи завернуть вPolicy объект, который передается вSetPolicy метод клиентского класса. Вот все, что мне нужно сделать для создания рабочего экземпляра клиента:

MyWebServiceWse api = new MyWebServiceWse();
api.Url = myUrl;
api.SetPolicy(new Policy(new MyDerivedSecurityAssertion(user, pass)));

Мой стандартный код для WCF (сгенерированный со ссылкой на службу) не принимает учетные данные, поэтому я знаю, что это проблема сразу же. Я читал в Интернете разные вещи об использовании разныхsecurity или обязательные настройки в моемapp.config, но ничего никогда не работало полностью. Моя самая распространенная ошибка после обильного ковырянияWSDoAllReceiver: Request does not contain required Security header.

Здесь app.config. Возможно, мы могли бы начать с того, чтобы рассказать мне, что следует изменить здесь, чтобы облегчить передачу полномочий - опять же, я видел различные мнения в Интернете.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="MySoapBinding" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://xyz:12345/services/MyService"
                binding="basicHttpBinding" bindingConfiguration="MySoapBinding"
                contract="MyNS.MyService" name="MyService" />
        </client>
    </system.serviceModel>
</configuration>

Я изменил некоторые атрибуты, чтобы скрыть конкретный сервис, который мы используем (политика компании и все такое).

И вот пример кода C # (тестирование в консольном приложении):

MyClient client = new MyClient();
client.listMethod();

UPDATE

Прочитайте это ТАК сообщение:ВКФ безопасности. , ,.

Я соответствующим образом обновил свой app.config и теперь передаю имя пользователя и pwd в коде. Я все еще получаю ту же ошибку:

WSDoAllReceiver: Request does not contain required Security header

20120517 UPDATE

Успешный запрос (от WSE3):

  <soap:Header>
    <wsa:Action>
    </wsa:Action>
    <wsa:MessageID>urn:uuid:cb739422-c077-4eec-8cb2-686837b76878</wsa:MessageID>
    <wsa:ReplyTo>
      <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:To>http://removed-for-security</wsa:To>
    <wsse:Security soap:mustUnderstand="1">
      <wsu:Timestamp wsu:Id="Timestamp-e13feaf9-33d9-47bf-ab5b-60b4611eb81a">
        <wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
        <wsu:Expires>2012-05-17T11:30:41Z</wsu:Expires>
      </wsu:Timestamp>
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-00c26e1a-3b3b-400f-a99a-3aa54cf8c8ff">
        <wsse:Username>change-to-protect-the-innocent</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nice-try</wsse:Password>
        <wsse:Nonce>KJMvUuWF2eO2uIJCuxJC4A==</wsse:Nonce>
        <wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <listChannels xmlns="http://removed-for-security">
      <rowfrom>0</rowfrom>
      <rowto>10</rowto>
    </listChannels>
  </soap:Body>
</soap:Envelope>

Работаю над получением трассы WCF - скоро добавлю.

20120517 UPDATE 2

А вот и конверт из WCF:

  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
      <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <listChannels xmlns="http://removed-for-security">
        <rowfrom>1</rowfrom>
        <rowto>2147483647</rowto>
      </listChannels>
    </s:Body>
  </s:Envelope>

20120518 UPDATE Я попытался реализовать решение в посте, на который Майк Миллер ссылается в комментариях. Теперь я получаю следующую ошибку (ни одно сообщение не заканчивается отправкой, потому что в схеме что-то не так):

The provided URI scheme 'http' is invalid; expected 'https'.

И если кто-то захочет спросить, да, мне нужно отправить по http, и да, я знаю, что учетные данные отправляются в виде незашифрованных строк :-)

Я думаю, что последнее обновление указывает на это направление. Нет учетных данных клиента уровня сообщения через http с wcf.stackoverflow.com/questions/9191104/… Yiğit Yener
Первое, что я бы сравнил, это запросы на мыло, генерируемые как WSE, так и amp; Клиенты WCF. Так как клиент WCF настроен дляsecurity mode="None" вероятно, заголовок безопасности мыла не создается, но захваченные запросы покажут это. Я считаю, что BasicHttpBinding не может быть настроен для поддержки безопасности на уровне сообщений. Скорее всего, вам понадобится wsHttpBinding для этой конфигурации. Наконец, посмотрите, что Microsoft сделала вWCF Express Interop проект, его мастера могут относиться и к клиентам WCF. Sixto Saez
Спасибо, Sixto - как лучше всего захватить запрос, который отправляет WCF? Brian Warshaw
Это та же проблема?stackoverflow.com/questions/3102693/… Mike Miller
WCF имеет встроенныйmessage tracing особенность, но вы также можете использоватьFiddler для захвата трафика от клиентов WSE и WCF. Чтобы расширить мой предыдущий комментарий, вам следует подойти к созданию вашего нового клиента WCF с точки зрения взаимодействия со службой Apache Axis вместо переноса с устаревшего клиента WSE. Несмотря на «взаимодействие» обещание стандартов WS- *, очень трудно заставить WCF работать с любой службой или клиентом, не основанным на WCF. Sixto Saez

Ваш Ответ

1   ответ
10

который не поддерживается в wcf ootb. Кроме того, ваш токен использует nonce / созданный, который также не является ootb. у вас есть 2 варианта:

this oss project adds nonce/created to the username token. this oss project adds the ability to send username over http. you would need to combine both projects together.

ws-security is usually considered complex, but you use it in its simplest form (username). the easiest would be to dismiss any wcf security setting all together and create the whole security header by yourself in a message inspector! As you can see most headers are just static xml nodes, and most values are pretty clear (you know the username). the only tricky two are the nonce and the timestamps which you could look how to do in this oss project (one line each). There is a variant of this option which may be easier - use CUB after all and implement a custom encoder which pushes the timestmpa/nonce. I would go for the latter but I'm biased since I developed CUB...

Также есть заголовки ws-адресации, которые вы можете настроить в своей пользовательской кодировке & quot; messageVersion & quot; имущество. Я не могу сказать точное значение, так как вы опустите заголовок конверта с определением префикса wsa.

Если вам нужна помощь в частном порядке (поскольку у вас, похоже, есть ограничения безопасности), обязательно отправьте мне электронное письмо отмой блог.

РЕДАКТИРОВАТЬ: Я реализовал это для вас. Следуй этим шагам:

download cub and make yourself familiar with it (not the internals, just how to use it according to the blog post)

add reference to System.Runtime.Serialization.dll to the project ClearUsernameBinding

add a new file to that project: UsernameExEncoder.cs. Paste this content:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.IO;
using System.Xml;
using System.Security.Cryptography;

namespace Webservices20.BindingExtensions
   {
    class UsernameExEncoderBindingElement : MessageEncodingBindingElement
    {
    MessageEncodingBindingElement inner;        

    public UsernameExEncoderBindingElement(MessageEncodingBindingElement inner)
    {
        this.inner = inner;            
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        var res = base.BuildChannelFactory<TChannel>(context);
        return res;
    }

    public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
    {
        var res = base.CanBuildChannelFactory<TChannel>(context);
        return res;
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        return new UsernameExEncoderFactory(this.inner.CreateMessageEncoderFactory());
    }      

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.inner.MessageVersion;
        }
        set
        {
            this.inner.MessageVersion = value;
        }
    }

    public override BindingElement Clone()
    {
        var c = (MessageEncodingBindingElement)this.inner.Clone();
        var res = new UsernameExEncoderBindingElement(c);
        return res;
    }

    public override T GetProperty<T>(BindingContext context)
    {
        var res = this.inner.GetProperty<T>(context);
        return res;
    }
}

class UsernameExEncoderFactory : MessageEncoderFactory
{
    MessageEncoderFactory inner;        

    public UsernameExEncoderFactory(MessageEncoderFactory inner)
    {
        this.inner = inner;            
    }

    public override MessageEncoder Encoder
    {
        get { return new UsernameExEncoder(inner.Encoder); }
    }

    public override MessageVersion MessageVersion
    {
        get { return this.inner.MessageVersion; }
    }

}

class UsernameExEncoder : MessageEncoder
{
    MessageEncoder inner;

    public override T GetProperty<T>()
    {
        return inner.GetProperty<T>();
    }

    public UsernameExEncoder(MessageEncoder inner)
    {
        this.inner = inner;
    }

    public override string ContentType
    {
        get { return this.inner.ContentType; }
    }

    public override string MediaType
    {
        get { return this.inner.MediaType; }
    }

    public override MessageVersion MessageVersion
    {
        get { return this.inner.MessageVersion; }
    }

    public override bool IsContentTypeSupported(string contentType)
    {
        return this.inner.IsContentTypeSupported(contentType);
    } 

    public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
    {
        return this.inner.ReadMessage(buffer, bufferManager, contentType);
    }

    public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
    {
        return this.inner.ReadMessage(stream, maxSizeOfHeaders, contentType);
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
    {   
        //load the message to dom
        var mem = new MemoryStream();
        var x = XmlWriter.Create(mem);
        message.WriteMessage(x);
        x.Flush();
        mem.Flush();
        mem.Position = 0;
        XmlDocument doc = new XmlDocument();
        doc.Load(mem);

        //add the missing elements
        var token = doc.SelectSingleNode("//*[local-name(.)='UsernameToken']");
        var created = doc.CreateElement("Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        var nonce = doc.CreateElement("Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        token.AppendChild(created);
        token.AppendChild(nonce);

        //set nonce value
        byte[] nonce_bytes = new byte[16];
        RandomNumberGenerator rndGenerator = new RNGCryptoServiceProvider();
        rndGenerator.GetBytes(nonce_bytes);
        nonce.InnerText = Convert.ToBase64String(nonce_bytes);

        //set create value
        created.InnerText = XmlConvert.ToString(DateTime.Now.ToUniversalTime(), "yyyy-MM-ddTHH:mm:ssZ");

        //create a new message
        var r = XmlReader.Create(new StringReader(doc.OuterXml));
        var newMsg = Message.CreateMessage(message.Version, message.Headers.Action, r);

        return this.inner.WriteMessage(newMsg, maxMessageSize, bufferManager, messageOffset);
    }




    public override void WriteMessage(Message message, System.IO.Stream stream)
    {
        this.inner.WriteMessage(message, stream);
    }
}
}

In the file ClearUsernameBinding.cs replace this:

res.Add(new TextMessageEncodingBindingElement() { MessageVersion = this.messageVersion});

with this:

var textEncoder = new TextMessageEncodingBindingElement() { MessageVersion = this.messageVersion }; res.Add(new UsernameExEncoderBindingElement(textEncoder));

In the project TestClient in app.config there is a messageVersion property on the binding element. You have not published the root of your envelope so I cannot know for sure, but probably you need to set it to Soap11WSAddressingAugust2004 or Soap11WSAddressing10 (or one of these with Soap12 instead).

Удачи!

Ярон - я получаю нарушение прокси-сервера здесь, в рабочей сети, пытаюсь получить код CUB (потому что это s @ blogspot). Есть ли у вас другие места, где код доступен? Brian Warshaw
код в гугл кодеcode.google.com/p/wcf-clear-username-binding блог просто содержит объяснение

Похожие вопросы