2009-07-27 5 views
3

ich einige Unit-Tests zu tun, und lief in ein interessantes Problem in WCF. Ich habe einen Dienst die wsHttpBinding verwenden, konfiguriert als solche:WCF AuthorizationContext wurde zwischen Anrufen von verschiedenen Clients nicht gelöscht. (War: Wann ist eine WCF-Dienst-Sitzung tatsächlich am Ende?)

<bindings> 
    <wsHttpBinding> 
    <binding name="wsHttpUnsecured"> 
     <security mode="None"> 
     <transport clientCredentialType="None" /> 
     <message clientCredentialType="None" /> 
     </security> 
    </binding> 
    </wsHttpBinding> 

Die Implementierung des Dienstes ist:

public void DoWork() 
{ 
    OperationContext o = OperationContext.Current; 
    if (o == null) return; 

    if (o.ServiceSecurityContext.AuthorizationContext.Properties.ContainsKey("x")) 
     throw new ApplicationException(); 

    o.ServiceSecurityContext.AuthorizationContext.Properties.Add("x", "x"); 
} 

Alles, was es hier tut, ist die Operation Kontext überprüft, und wenn es nicht ist "X" im AuthorizationContext, dann fügen Sie einen hinzu. Wenn es bereits ein "X" gab, dann Ausnahme. (Dies ist streng als ein einfacher Test eingerichtet. Im normalen Gebrauch würde dies in einem benutzerdefinierten AuthorizationManager und Token Authenticator passieren).

Also, im Grunde, wenn wir die Methode mehr als einmal innerhalb der gleichen OperationContext und AuthorizationContext aufrufen, dann werden wir eine Ausnahme sehen.

Jetzt ist hier meine Testvorrichtung.

[Test] 
    public void CallTwice() 
    { 
     using (var cli1 = new TestBusinessClient()) 
     { 
      cli1.DoWork(); 
      cli1.Close(); 
     } 
     using (var cli2 = new TestBusinessClient()) 
     { 
      cli2.DoWork(); 
      cli2.Close(); 
     } 
    } 

So zu Fuß durch, was zur Laufzeit passiert:

  1. Ein neues TestBusinessClient erstellt wird.
  2. Es macht einen Aufruf an DoWork().
  3. DoWork() findet nicht "X" in AuthorizationContext.Properties.
  4. DoWork() fügt "X" zu AuthorizationContext.Properties hinzu.
  5. Die Testmethode verfügt über den ersten Client.
  6. Ein neues zweiten TestBusinessClient erstellt.
  7. Es macht einen Aufruf an DoWork().
  8. DoWork()findet "X" immer noch in den Eigenschaften des letzten Anrufs.
  9. DoWork() löst eine Ausnahme.

Also meine Frage ist; Warum werden OperationContext und AuthorizationContext nicht deaktiviert, wenn ein neuer Client eine Verbindung mit demselben Dienst herstellt? Ich verstehe, dass wsHttpBinding standardmäßig einen Sitzungskontext zwischen den Anrufen verwendet, aber ich würde denken, dass Sitzung pro Client wäre. Ich habe erwartet, dass die WCF-Sitzung und damit ihre Kontexte alle erneuert werden, wenn ich mich mit einer neuen Instanz eines Clients verbinde.

Wer noch keine Gedanken oder Ideen? Das gewünschte Ergebnis ist hier, dass AuthorizationContext.Properties zwischen den zwei Aufrufen von den zwei getrennten Clients zu DoWork() zurückgesetzt wird.


aktualisieren 1

Ich habe versucht, den Dienst PerCall und PerSession Einstellung, und weder einen Unterschied gemacht.

Ich habe auch den Dienst mit zuverlässigen Messaging ein- und ausgeschaltet, und nichts geändert.

Ich sparte auch meine Operation beim ersten Anruf und den zweiten Anruf aus und verglichen die beiden:

OperationContext first; // context from first call to DoWork() 
OperationContext second; // context from second call to DoWork() 

(first == second) = false 
(first.ServiceSecurityContext == second.ServiceSecurityContext) = false 
(first.ServiceSecurityContext.AuthorizationContext == second.ServiceSecurityContext.AuthorizationContext) = true 

es also eine Art, wie die Operation Kontext aussieht wird/neu geändert, aber etwas setzt die Gleiches AuthorizationContext bei jedem folgenden Service-Anruf.


Update 2

Hier ist alles, das serverseitige Material:

[ServiceContract] 
public interface ITestBusiness 
{ 
    [OperationContract(Action = "*", ReplyAction = "*")] 
    string DoWork(); 
} 

public class TestBusiness : ITestBusiness 
{ 
    public string DoWork() 
    { 
     System.ServiceModel.OperationContext o = System.ServiceModel.OperationContext.Current; 
     if (o != null) 
     { 
      if (o.ServiceSecurityContext.AuthorizationContext.Properties.ContainsKey("x")) 
       throw new ApplicationException(); 
      else 
       o.ServiceSecurityContext.AuthorizationContext.Properties.Add("x", "x"); 
     } 
    } 
    return ""; 
} 

Als Plausibilitätsprüfung, ich habe folgendes:

  1. Starten einer Instanz des WCF-Servers (mit Cassini/integriertem VS 2008-Server).
  2. Reduzieren Sie die Testvorrichtung auf nur 1 Anruf an DoWork().
  3. Führen Sie den Test von TestDriven.NET innerhalb VS 2008
  4. Öffnen Sie den gleichen Test .dll von NUnit's Standalone-GUI-Tool, und es laufen.

Der erste Testlauf läuft, und der zweite schlägt fehl. Es scheint also, dass dies rein serverseitig ist, da das Ausführen desselben Serviceanrufs von zwei verschiedenen Prozessen mit demselben AuthorizationContext endet.

Ich beginne mich zu fragen, ob vielleicht etwas internes zu WCF immer noch auf WindowsAuthentication stecken und das gleiche Auth Kontext verwenden, da ich in der gleichen Domäne mit dem gleichen Benutzernamen angemeldet bin? Mein Service wird eine benutzerdefinierte AuthorizationManager verwenden: ServiceAuthorizationManager

 <serviceBehaviors> 
      <behavior name="myBehavior"> 
       <serviceMetadata httpGetEnabled="false" /> 
       <serviceDebug includeExceptionDetailInFaults="true" /> 
       <serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="My.Namespace.CustomAuthMgr" /> 
      </behavior> 

Wo My.Namespace.CustomAuthMgr erstreckt. Wenn ich einen Haltepunkt in CustomAuthMgr.CheckAccess() einstelle, dann ist beim ersten Anruf operationContext.ServiceSecurityContext.AuthorizationContext gelöscht, und beim zweiten Anruf ist alles enthalten, was ich vom vorherigen Anruf eingegeben habe. Dies ist die erste Methode meines eigenen Codes, die von WCF ausgeführt wird, also etwas vor der Autorisierungsphase lädt meine AuthorizationContext.


Update 3

Einige hinzugefügt Info: Um einige Dinge zu überprüfen, ich meine Service-Implementierung ändert sich nicht mehr, eine Ausnahme zu werfen und stattdessen einen Zähler der Anzahl der Male zurückkehren genannt, plus die aktuelle Thread-ID:

public string DoWork() 
    { 
     var o = System.ServiceModel.OperationContext.Current.ServiceSecurityContext.AuthorizationContext; 
     if (o != null) 
     { 
      if (o.Properties.ContainsKey("x")) 
       o.Properties["x"] = (int)o.Properties["x"] + 1; 
      else 
       o.Properties.Add("x", 1); 
     } 

     return o.Properties["x"].ToString() + " | " + System.AppDomain.GetCurrentThreadId().ToString(); 
    } 

Dann läuft der Test einmal von NUnit GUI ergibt:

1 | 3816 

ich dann schließen Sie die NUnit GUI, starten Sie ihn neu, und führen Sie den Test erneut:

2 | 3816 

ich die NUnit GUI dann wieder schließen und den Test von TestDriven.NET in Visual Studio ausführen:

3 | 3816 

so persistierenden sein meine AuthorizationContext zwischen Client definitiv Prozessen, aber der gleiche Thread behandelt jeden Service-Aufruf, vielleicht AuthorizationContext ist gerade Thema statisch oder so etwas?

Nein, es hat nichts mit dem Thread zu tun. Ich habe ein Thread.Sleep(10000); an die Dienstimplementierung, lief dann 2 NUnit GUIs auf einmal, und jeder ausgedruckt „2“, aber mit unterschiedlichem Thread-IDs:

2 | 5500 
2 | 5764 

So AuthorizationContext auch über Threads beibehalten wird. Flott.

+0

Können Sie uns den Servicevertrag (Serverseite) und die Implementierung des Dienstes zeigen (nur die Klassendefinition mit möglichen Attributen) –

Antwort

1

Das hängt wirklich davon ab, wie Sie Sitzung und Client definieren. Wenn Sie zwei Aufrufe von demselben Prozess ausführen, gibt es alle Möglichkeit, dass die WCF-Kommunikationsschicht sie über dieselbe Verbindung multiplext.

Haben Sie tatsächlich versucht, zwei Verbindungen von völlig verschiedenen Prozessen auszuführen? Erzielen Sie dieselben Ergebnisse?

+0

Ich habe versucht, den Dienst (in Cassini/VS 2008 integriertem Webserver) dann zu starten Einmaliges Ausführen des Komponententests, der die Sitzung beendet und die Nunit-Sitzung beendet. Dann wurde der Komponententest ein zweites Mal ausgeführt, und es schlägt fehl. Ich habe diese 2 Back to Back mit TestDriven.NET in VS 2008. Ich öffnete dann NUnit GUI-Tool (extern zu VS2008) geladen die gleiche Test. DLL, und der Test immer noch fehlgeschlagen, alle auf dem gleichen Server ausgeführt. Also 3 Testläufe, von 2 verschiedenen Prozessketten, und nur die erste läuft. – CodingWithSpike

+0

Ich bin auch von Ihren Ergebnissen überrascht. Eine gute Möglichkeit, dies nachzuverfolgen, besteht möglicherweise darin, die Sicherheitsüberwachung in WCF zu aktivieren. Es ist nur ein Verhalten, das Sie Ihrem Dienst anhängen können. Der feault ist AuditLevel.None, aber wenn Sie es hoch drehen, könnte es Ihnen mehr Transparenz darüber geben, was wann passiert. – Christopher

1

Es ist auf der Konfiguration Ihres Service abhängt, welchen Wert Sie für Ihre Session wählen:

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)] 
public interface ICalculatorSession 

http://msdn.microsoft.com/en-us/library/ms733040.aspx

http://msdn.microsoft.com/en-us/library/system.servicemodel.sessionmode.aspx

+0

Ich habe versucht SessionMode.NotAllowed und SessionMode.Required, ohne Unterschied. – CodingWithSpike

+0

Sie müssen auch InstanceContextMode festlegen, dies kann InstanceContextMode.PerSession/PerCall/single sein, der zweite Link enthält Informationen dazu. –

+0

Ich probierte sowohl PerSession und PerCall ohne Änderung des Ergebnisses. Wie in meinem Update # 2 oben erwähnt, habe ich auch begonnen, den Dienst einmal aus zwei verschiedenen Prozessen aufzurufen, und immer noch das gleiche Ergebnis. – CodingWithSpike

1

WsHttp von Standardlauf im Sitzungsmodus Bindung Willen, da die Sicherheit ist standardmäßig aktiviert. Wenn Sie es vollständig ausschalten (wie Ihre Konfiguration zu zeigen scheint), kehrt es in den Modus für Anrufe zurück, es sei denn, Ihr Dienstvertrag erfordert speziell eine Sitzung.

Die Sitzung wird so lange dauern, da keine der Sitzung Timeouts aufgetreten sind - Sie ein auf dem Server definieren kann, und eine andere auf dem Client, und die kürzere „gewinnt“. Der einfachste Weg, es zu spezifizieren, durch die zuverlässige Messaging zum wsHttpBinding fügte hinzu:

<wsHttpBinding> 
    <binding> 
     <reliableSession inactivityTimeout="00:10:00"/> 
    </binding> 
    </wsHttpBinding> 

Sonst wirst du es im Code zu tun haben, oder indem Sie Ihre benutzerdefinierte Bindung zu definieren.

ODER: Sie können auch bestimmte Methoden Ihres Servicevertrags als "beendende" Methoden definieren. Wenn diese Methoden aufgerufen und beendet wurden, wird die Sitzung ebenfalls gelöscht. Hier

[ServiceContract(SessionMode=SessionMode.Required)] 
interface YourServiceContract 
{ 
    [OperationContract(IsInitiating = true)] 
    void InitMethod(); 

    [OperationContract] 
    void WorkMethod() 

    [OperationContract(IsTerminating=true)] 
    void EndSessionMethod() 
} 

, ruft die EndSessionMethod die Sitzung und nicht mehr Anrufe beenden können auf der speziellen Proxy-Instanz in den Dienst gestellt werden.

ODER: Eine nicht behandelte Ausnahme auf der Serverseite, die nicht abgefangen und in eine FaultException umgewandelt wird, führt ebenfalls zu einer Störung des Kanals und zum Abbrechen einer möglicherweise laufenden Sitzung.

Aber wie Sie darauf hinweisen - auch ich hätte erwartet, dass die gesamte Sitzung und alle damit verbundenen Daten verworfen werden, wenn der Client-Proxy entsorgt und für einen nachfolgenden Aufruf neu erstellt wird. Ich bin ein wenig durch Ihre Ergebnisse überrascht ....

Marc

+0

Also, @marc_s: Verwenden Sie fälschlicherweise einen "using" -Block, da das OP die Sitzung nicht automatisch beendet? –

+0

Nun, das hätte ich erwartet, und das OP auch, aber es scheint, dass es den OperationContext und/oder den AuthorizationContext nicht wirklich auf dem Server entsorgt –

+0

Aber ich stimme zu - während die 'using' Anweisung normalerweise eine gute Idee ist Im WCF-Land kann es ein wenig trügerisch sein zu glauben, dass es wie gewohnt funktioniert - aber das ist schon fortgeschrittenes Zeug :-) –

0

ich ähnliches Problem haben - ich sehe, dass AuthorizationContext bleibt gleich nach jedem Anruf.

Ich habe bemerkt, dass Sie in Ihrer Konfigurationsdatei

<serviceAuthorization principalPermissionMode="Custom" .... 

Try principalPermissionMode = "Custom" zu entfernen haben. Zumindest in meinem Fall kommt jeder neue Aufruf mit null AuthorizationContext.

Verwandte Themen