User Tools

Site Tools


patterns:inheritancerolestragegyoverview:strategy

This is an old revision of the document!


Das Strategie Muster

Strategien sind mächtige und sinnvolle Werkzeuge für Verhalten, das austauschbar ist. Zudem kann das Verhalten von allen abgeleiteten Klassen geerbt werden. Ein tolles Beispiel hierfür ist die Authentifizierung eines Kontos. Es gibt diverse Möglichkeiten1) wie ein Konto authentifiziert werden kann. Beispiele sind:

  • über die Angabe eines Benutzernamens und eines Kennwortes,
  • über eine vorhandene Authentifizierung, z.B. wenn ein Anwender bereits an Windows angemeldet ist,
  • über ein X.509 Zertifikat,
  • über einen Dongle,
  • gar nicht,
  • usw.

Wenn die Authentifizierung als Strategie umgesetzt wird, kann man sich erst mal für eine entscheiden. Die anderen können dann bei Bedarf dazu gebaut werden, und in einigen Fällen werden neue Strategien dazu kommen, die zum jetzigen Zeitpunkt noch gar nicht bekannt waren.

Eine Strategie wird vorbereitend in ein Aggregate eingebaut2). Sie wird abstrakt definiert. Sie wird beim Erzeugen des Aggregates gesetzt und kann danach nicht mehr verändert werden3). Wie das Ganze in CQRS/ES funktioniert, kann am besten an einem Beispiel erläutert werden.

Die Benutzernamen Strategie zur Authentifizierung

  • Der Befehl, um ein Konto zu erzeugen, ist AddAccount(Guid id, AuthenticationStrategyDTO strategy). Dieser Befehl ist abstrakt, und von AddUserAccount implementiert werden. AddUserAccount würde wie folgt erzeugt werden:
    new AddUserAccount(new Guid("3F2504E0-4F89-11D3-9A0C-0305E82C3301"), PersonId, new UserNameStrategyDTO("abc", "Kennwort789"));
  • Der Command Handler ruft im Konto4) den Konstruktor wie folgt auf5)
    var accnt = new UserAccount(cmd.Id, cmd.PersonId, cmd.strategy);
  • Im Konstruktor von UserAccount wird nach allen Prüfungen das Ereignis UserAccountAdded(Guid id, Guid personId, AuthenticationStrategyDTO strategy) veröffentlicht über
    ApplyEvent(new UserAccountAdded(Id, PersonId, strategy));
  • Dabei kann6) das Kennwort kein einfacher string sein, sondern ein cryptedString7), der verschlüsselt in den Befehl und in das Domänenereignis geschrieben wird,
  • In dem UserAccount wird in Apply(UserAccountAdded @event) aus @event.strategy eine AuthenticationStrategy, ganz einfach über
    new AuthenticationStrategy(@event.UserName, Decrypt(@event.Password));

    also kein DTO das8) in Befehle und Ereignisse geschrieben wird, sondern ein Strategie Objekt mit einem entsprechenden Verhalten.

Was die Projektions-Seite angeht, läuft das ähnlich wie bei der Vererbung. Wenn man für die AuthenticationStrategy gemeinsame Eigenschaften identifizieren kann, kann man diese über einen weiteren Info9)-Kontext implementieren, und für jede Implementierung der Authentifizierungs Strategie müsste dieser Kontext befüllt werden.

Die Strategie verwenden

Wie kann eine Authentifizierungs Strategie verwendet werden? Authentifiziert werden immer die credentials10). Es muss also die abstrakte Klasse Credentials geben. Das UI erzeugt die entsprechenden Credentials, und sendet dann einen AuthenticateAccountCredentials Befehl an die Domäne11), z.B.

authenticationProxy.Apply(new AuthenticateAccountCredentials(accountId, textBoxPassword.Text));

Der Command Handler macht dann folgendes

public void Handle(AuthenticateAccountCredentials command)
{
    var repo = new Repository<Account>();
    var agg = repo.Get(command.Id);
    agg.Authenticator.Authenticate(command.Credentials);
    repo.Save(agg);    
}

In diesem Fall ist somit nicht nur die Strategie austauschbar, sondern auch die Information, die jede Strategie zur Prüfung benötigt. Möglicherweise sind die Anmeldeprozesse so unterschiedlich12), dass der Command Handler abhängig des Typs der Credentials noch über Service Locator einen weiteren Dienst aufruft, der weiß, wie die Authentifizierung im Einzelnen ablaufen muss.

Kerberos Authentifizierung

Die Kerberos Authentifizierung läuft in mehreren Schritten ab. Das sollte aber wie folgt möglich sein:

  1. über den Benutzernamen fragt das Front-End nach der Account-ID,
  2. ein Lesedienst fragt die denormalisierten Kerberos Konten ab13) und sendet das Ergebnis zurück an den Client,
  3. der Client sendet eine Anfrage mit der ID nach dem verschlüsselten AuthenticationKey und dem AuthenticationTicket an die Query-Seite des Kontos14),
  4. Der Kerberos Query Dienst lädt das Konto, wandelt die Strategie in eine KerberosAuthenticationStrategy, und fragt sie nach den angeforderten Daten15),
  5. Der Client entschlüsselt mit dem Kennwort den AuthenticationKey, verschlüsselt damit die ID und das Timeout, das mit dem letzten Aufruf zurück erhalten wurde, und sendet diese beiden zusammen mit dem AuthenticationTicket16) als KerberosCredentials an das Konto als Authenticate Befehl,
  6. Hinweis17): Der AuthenticateCredentials Befehl ist abstrakt. Das ist für Befehle für Strategien generell sinnvoll. Es wird dann einen Command Handler geben, der AuthenticateKerberosCredentials Befehle behandelt. Dieser wird dann das Konto über das Repository laden, und eine entsprechende Methode zur Authentifizierung aufrufen. Dieser Methode können neben den Credentials dann weitere Dinge übergeben werden, wie z.B. die ICryptTickets Schnittstelle,
  7. Der Command Handler aus dem vorherigen Punkt lädt also das Konto, und ruft die entsprechende Authenticate Methode der Authenticate Strategie auf18). Wenn das klappt, wird ein Authenticated Ereignis veröffentlicht, das einen SessionKey enthält, verschlüsselt mit dem AuthenticationKey, und ein neues SessionTicket19),
  8. Wenn das nicht klappt, wird NotAuthenticated Ereignis ausgelöst. Nach 5 Versuchen wird das Konto gesperrt,
  9. Der Client pollt mitlerweile nach dem Ergebnis der Anmeldung. Erhält er ein positives Ergebnis, ist er angemeldet. Er verschlüsselt dann die UserId und das Timeout mit dem erhaltenen SessionKey, den er vorher mit dem AuthenticationKey entschlüsseln muss, und sendet dieses gemeinsam mit dem SessionTicket in jedem Aufruf an den Server mit,
  10. Der Server braucht jetzt das Konto nicht mehr zu laden um den Dienstaufruf zu authentifizieren. Anhand der mit gesendeten Credentials wird über Service Locator ein IAuthenticateCredentials Dienst geladen. Der Dienst für die Kerberos Credentials kann das von ICryptTickets verschlüsselte Ticket entschlüsseln20) und mit dem darin enthaltenen SessionKey die vom Client mit dem SessionKey verschlüsselten Daten ermitteln, und die UserId und das Timeout in beiden vergleichen. Stimmt alles, dann kennt der Client den SessionKey, was er ja nur kann, wenn er ihn mit dem AuthenticationKey entschlüsseln konnte, den er vorher mit dem Passwort21) entschlüsseln konnte.
1) Strategien
2) Oder in eine neue Rolle zu dem Aggregate, falls der Bereich komplett neu dazu gebaut wird
3) wie sollten die historischen Domain Events von einer Strategie von einer anderen verwendet werden?
4) UserAccount
5) cmd ist der AddUserAccount Befehl
6) sollte
7) eigener Typ
8) teil-verschlüsselt
9) oder Lese
10) Berechtigungsnachweis
11) nachdem das Konto ermittelt wurde, das authentifiziert werden soll, z.B. über den Benutzernamen
12) z.B. Kerberos mit mehreren Nachrichten, die hin-und-her gesendet werden
13) SELECT ID FROM KerberosAccount WHERE UserName=@UserName
14) hierfür muss ein Kerberos Query Dienst eingerichtet sein
15) , 18) wobei eine ICryptTickets Schnittstelle in dem Methdenaufruf mitgegeben wird
16) das vom Client nicht entschlüsselt werden kann
17) noch sauberer in den Workflow einbauen, ist mir gerade eingefallen
19) die Tickets sind durch ICryptTickes verschlüsselt, so dass es auch nicht vom Client entschlüsselt werden kann. Sie enthalten u.a. die UserId, das Timeout und den Key. Mit dem Key werden die vom Client verschlüsselten UserId und Session-Timeout Daten entschlüsselt, um zu prüfen, ob der Client das alles mit dem korrekten Schlüssel verschlüsselt hat - den Schlüssel kann der Client ja nur haben, wenn er initial das Passwort zur entschüsselung kannte.
20) Er ist i.d.R. der Dienst, der als ICryptTickets an das Konto übergeben wurde
21) mit dem MD5Hash vom Passort
patterns/inheritancerolestragegyoverview/strategy.1357685841.txt.gz · Last modified: 2013/01/08 23:57 by rtavassoli