2012-05-14 2 views
10

Ich bin überall im Netz dafür gewesen. Ich habe es einfach eine Menge Zeit damit verbracht, und der Anbieter, dessen Web-Service ich versuche zu konsumieren, lehnt es ab, WCF offiziell als eine Methode des Konsums zu unterstützen.(Versuch zu) Migration von WSE 3.0 zu WCF für Client-Code

Ich bin kein Web-Service-Experte, also werde ich mein Bestes geben, um zu dokumentieren und zu erklären mit diesem ersten Beitrag, aber auf alle Fälle, weitere Informationen anfordern, wenn Sie es brauchen, und hoffentlich werde ich liefern können was auch immer notwendig ist.

Der Service

In meiner Firma verwenden wir einen Anbieter-Anwendung, die einen Dienst bereitstellt. Die Anwendung wird in Java geschrieben und es sieht so aus, als ob die WSDL mit Apache Axis 1.2 erstellt wurde.

Der Code

verwendet My Legacy-Code 3.0 WSE. Insbesondere verwendet es die Proxy-Klassen, bei denen "WSE" am Ende automatisch angeheftet ist. Dies erlaubt mir ein viel einfacheres Authentifizierungsschema (die einzige Möglichkeit, es zum Laufen zu bringen). Ich brauche keine Zertifikate. Ich verwende eine Ableitung von SecurityPolicyAssertion und wickle sie in ein Policy-Objekt, das an die SetPolicy-Methode der Clientklasse übergeben wird. Hier ist alles, was ich tun müssen, um eine funktionierende Instanz des Clients zu erstellen:

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

Mein Standard, out-of-the-box-Code für WCF (erzeugt mit einer Service-Referenz) Anmeldeinformationen nicht akzeptieren, damit ich weiß, Es gibt ein Problem auf Anhieb. Ich habe verschiedene Dinge online über die Verwendung unterschiedlicher security oder verbindliche Einstellungen in meinem app.config gelesen, aber nichts hat jemals vollständig funktioniert. Mein häufigster Fehler nach reichlichem Basteln ist WSDoAllReceiver: Request does not contain required Security header.

Hier ist die app.config. Vielleicht könnten wir damit beginnen, mir zu sagen, was sich hier ändern sollte, um die Übertragung der Zeugnisse zu erleichtern - wieder habe ich unterschiedliche Meinungen online gesehen.

Ich habe einige der Attribute geändert, um den spezifischen Dienst zu verschleiern, den wir verwenden (Firmenrichtlinie und all das).

Und hier ist der C# Codebeispiel bisher (Tests in einer Konsolenanwendung):

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

UPDATE

dieses SO Beitrag lesen: wcf security . . ..

Ich habe meine app.config entsprechend aktualisiert und übergebe nun Benutzername und PWD in Code. Ich erhalte immer noch die gleichen Fehler:

WSDoAllReceiver: Request does not contain required Security header 

20120517 UPDATE

Eine erfolgreiche Anforderung (von 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> 

Arbeiten an den WCF-rückverfolgbar bekommen - wird in Kürze hinzugefügt.

20120517 UPDATE 2

Und hier ist der Umschlag von 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 ich die Lösung in der Post versucht haben, die Umsetzung, die Mike Miller in den Kommentaren verbindet. Jetzt erhalte ich die folgende Fehlermeldung (keine Meldung endet geschickt bekommen, weil etwas von barfing auf dem Schema):

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

Und falls jemand will, zu fragen, ja, ich brauche http zu senden über, und ja, ich Beachten Sie, dass Anmeldeinformationen als unverschlüsselte Zeichenfolgen gesendet werden :-)

+0

Das erste, was ich vergleichen würde, ist die Soap-Anfragen von den beiden WSE & WCF-Clients generiert. Da der WCF-Client für 'security mode =" None "konfiguriert ist, wird wahrscheinlich kein Soap-Sicherheitsheader erstellt, aber die erfassten Anforderungen zeigen dies an. Ich glaube, das basicHttpBinding kann nicht so konfiguriert werden, dass es die Sicherheit auf Nachrichtenebene unterstützt. Sie werden wahrscheinlich die wsHttpBinding für diese Konfiguration benötigen. Sehen Sie sich schließlich an, was Microsoft im Projekt [WCF Express Interop] (http://wcf.codeplex.com/wikipage?title=WCF%20Express%20Interop%20Bindings) ausgeführt hat. Die Assistenten können auch auf WCF-Clients angewendet werden. –

+0

Danke, Sixto - was ist der beste Weg, um die Anfrage zu erfassen, die WCF sendet? –

+0

WCF verfügt über eine integrierte [Nachrichtenverfolgung] (http://msdn.microsoft.com/en-us/library/ms730064.aspx) Funktion, aber Sie könnten auch [Fiddler] (http: //www.fiddler2. com/fiddler2 /) für die Erfassung von Datenverkehr von WSE- und WCF-Clients. Um meinen vorherigen Kommentar zu erweitern, sollten Sie Ihren neuen WCF-Client aus der Sicht der Schnittstellen mit einem Apache Axis-Dienst erstellen, anstatt von einem alten WSE-Client aus zu portieren. Trotz des "Interop" -Versprechens der WS-Standards ist es ein echter Schmerz, WCF dazu zu bringen, mit jedem nicht-WCF-basierten Dienst oder Client zu arbeiten. –

Antwort

10

Was Sie brauchen, ist ein Benutzername-Token über HTTP-Transport senden, die nicht in wcf ootb unterstützt wird. Zusätzlich verwendet Ihr Token nonce/created, was auch nicht ootb ist. Sie haben 2 Möglichkeiten:

  1. diese oss project fügt dem Benutzernamen Token nonce/erstellt. Diese oss project fügt die Fähigkeit hinzu, den Benutzernamen über http zu senden. Sie müssten beide Projekte miteinander kombinieren.

  2. WS-Sicherheit wird normalerweise als komplex betrachtet, aber Sie verwenden es in seiner einfachsten Form (Benutzername). Am einfachsten wäre es, alle Sicherheitseinstellungen zu verwerfen und den gesamten Sicherheits-Header selbst in einem message inspector zu erstellen! Wie Sie sehen können, sind die meisten Header nur statische XML-Knoten und die meisten Werte sind ziemlich klar (Sie kennen den Benutzernamen). Die einzigen kniffligen zwei sind die Nonce und die Zeitstempel, die Sie in diesem oss project (je eine Zeile) schauen könnten. Es gibt eine Variante dieser Option, die einfacher sein kann - verwenden Sie CUB schließlich und implementieren Sie eine custom encoder, die das Timestmpa/Nonce schiebt. Ich würde für letztere gehen, aber ich bin voreingenommen, da ich CUB entwickelt ...

Es gibt auch die WS-Addressing-Header, die Sie auf Ihre benutzerdefinierte Codierung „Message“ Eigenschaft konfigurieren. Ich kann den genauen Wert nicht angeben, da Sie den Umschlagkopf mit der Wsa-Präfixdefinition weggelassen haben.

Wenn Sie Hilfe privat (da Sie scheinen, Sicherheitsbeschränkungen) auf alle Fälle senden Sie mir eine E-Mail von my blog.

EDIT: Ich habe es für Sie implementiert. gehen Sie folgendermaßen vor:

  1. herunterladen cub und machen Sie sich damit vertraut (nicht die Interna, nur wie man es benutzt nach dem Blog-Post)

  2. hinzufügen Verweis auf System.Runtime.Serialization.dll zu dem Projekt ClearUsernameBinding

  3. fügen Sie dem Projekt eine neue Datei hinzu: UsernameExEncoder.cs. Fügen Sie diesen Inhalt:

    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); 
        } 
    } 
    } 
    
  4. in der Datei ClearUsernameBinding.cs ersetzen diese:

    res.Add (neu TextMessageEncodingBindingElement() {Message = this.messageVersion});

    mit diesem:

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

  5. Im Projekt TestClient in app.config gibt es eine Eigenschaft messageVersion auf dem Bindungselement. Sie haben nicht die Wurzel Ihres Umschlags veröffentlicht, so dass ich nicht sicher wissen kann, aber wahrscheinlich müssen Sie es auf Soap11WSAddressingAugust2004 oder Soap11WSAddressing10 (oder eine davon mit Soap12 statt).

Viel Glück!

+0

Yaron - Ich bekomme hier eine Proxy-Verletzung im Arbeitsnetzwerk, um den CUB-Code zu bekommen (weil es @ blogspot ist). Haben Sie andere Standorte, an denen der Code verfügbar ist? –

+0

der Code ist in Google Code http://code.google.com/p/wcf-clear-username-binding/ der Blog enthält nur die Erklärung –