2010-02-19 13 views
5

initialisieren Ich habe eine funktionierende benutzerdefinierte UserNamePasswordValidator, die in meine Oracle DB aufruft.WCF Custom Validator: Wie ein "Benutzer" -Objekt aus benutzerdefinierten Validator

Diese Klasse wird von System.IdentityModel.Selectors.UserNamePasswordValidator abgeleitet und die Validate() -Methode gibt void zurück.

Ich lade mein Benutzerobjekt aus der Datenbank, und sobald das Passwort validiert ist, möchte ich mein "Benutzer" -Objekt speichern, damit der Dienst darauf zugreifen kann, wenn es seinen Geschäften nachgeht. In ASP.NET/Java lande ich es in eine Sitzung oder vielleicht meine gesamte Controller-Klasse. Wie mache ich das vom Validator in WCF?

Oder mit anderen Worten, was ist die beste Vorgehensweise in WCF Land, um ein benutzerdefiniertes Benutzerdomänenobjekt für den Dienst festzulegen.

Update: So habe ich gearbeitet. Ich speichere das Benutzerobjekt während des Validators zwischen und speichere es später im AuthorizatinPolicy-Schritt.

// this gets called after the custom authentication step where we loaded the User 
    public bool Evaluate(EvaluationContext evaluationContext, ref object state) 
    { 
    // get the authenticated client identity 
    IIdentity client = GetClientIdentity(evaluationContext); 

    User user; 
    OraclePasswordValidator.users.TryGetValue(client.Name, out user); 
    if(user != null) { 
     // set the custom principal 
     evaluationContext.Properties["Principal"] = user; 
     return true; 
    } 

    return false; 
    } 
+0

Ich lade jetzt mein User-Objekt (IPrincipal) in den Passwort-Validator, speichere es in einem statischen Dictionary und nehme es in die AuthorizationPolicy. Siehe oben Bearbeiten. Ist das der beste Weg? Sicher scheint wie ein Klotz für solch einen reichen Rahmen. – codenheim

+0

Nun, ich habe das Format in meiner Bearbeitung oben vermasselt und kann es nicht beheben. Das sollte eine vollständige Methode oben im Code-Bereich sein. – codenheim

Antwort

4

Ich bin kein WCF-Experte, aber von dem, was ich bisher gelesen und umgesetzt werden, der ‚richtige‘ Weg, dies zu tun wäre, die Validator zu beglaubigen die Benutzer zu verwenden, und implementieren Sie dann eine IAuthorizationPolicy, um die tatsächliche Autorisierung zu tun. In der Autorisierungsrichtlinie würden Sie also Ihren benutzerdefinierten Prinzipal für den aktuellen Thread festlegen.

Um Informationen aus der Benutzernamen-/Kennwortvalidierung weiterleiten zu können, können Sie einen Sicherheitstokenauthentifikator implementieren, der von UserNameSecurityTokenAuthenticator erbt. Der SecurityTokenAuthenticator ruft zuerst den Validator auf und wenn die Validierung erfolgreich ist, kann er Ihre benutzerdefinierte Autorisierungsrichtlinie hinzufügen und userinfo über den Konstruktor an die Richtlinie senden. Etwas ein langes die Linien davon:

public class CustomUsernameSecurityTokenAuthenticator : UserNameSecurityTokenAuthenticator 
{ 
    protected override bool CanValidateTokenCore(System.IdentityModel.Tokens.SecurityToken token) 
    { 
     return (token is UserNameSecurityToken); 
    } 

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token) 
    { 
     var authorizationPolicies = new List<IAuthorizationPolicy>(); 

     try 
     { 
      var userNameToken = token as UserNameSecurityToken; 
      new CustomUserNameValidator().Validate(userNameToken.UserName, userNameToken.Password); 

      var claims = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userNameToken.UserName, Rights.PossessProperty)); 

      authorizationPolicies.Add(new CustomAuthorizationPolicy(claims)); 
     } 
     catch (Exception) 
     { 
      authorizationPolicies.Add(new InvalidAuthorizationPolicy()); 
      throw; 
     } 
     return authorizationPolicies.AsReadOnly(); 
    } 
} 

Es gibt einen Artikel hier, der ein bisschen mehr um die beteiligten Klassen beschreibt; http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx

+0

Ich werde deine Antwort näher betrachten und es versuchen, und die Frage aktualisieren. Wenn Sie den Benutzer (Principal) in eine Karte einspeichern, löste sich das Problem sofort, und ich hatte noch keine Zeit, diesen Codeabschnitt noch einmal zu lesen, aber Ihre ist die erste plausible Erklärung, die ich gesehen habe. – codenheim

+0

Wie haken Sie das in die Pipeline? Sie verwenden auch CustomUserNameValidator, haken Sie es auch in der Konfigurationsdatei ein. Wenn ja, heißt es nicht zweimal? Gibt dieser Ansatz eine Anforderung für die Authentifizierung an den Client zurück (falls Basic Auth verwendet wird)? –

+0

FEHLER: CustomUsernameSecurityTokenAuthenticator implementiert nicht geerbtes abstraktes Member 'System.IdentityModel.Selectors.UserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore (string, string)' – Nuzzolilo

1

Ich habe genau das gleiche Problem.

Ich verwende eine API, um eine Verbindung zu meiner zugrunde liegenden Oracle-Datenbank herzustellen, und ich "validiere" Anmeldedetails, indem ich eine Verbindung öffne.

Ich möchte dann diese Verbindung irgendwo speichern (einfach genug, ich werde einen Verbindungspool für die verschiedenen Benutzer erstellen), sondern auch eine benutzerdefinierte Identität und Principal für diesen Benutzer erstellen, so dass es zu meiner benutzerdefinierten IAuthorizationPolicy Es muss diese Informationen nicht erneut laden.

Ich habe eine Menge von Such getan und nichts gefunden, so mein Plan, dies zu tun ist:

  1. Validate Login-Daten in benutzerdefinierter UserNamePasswordValidator durch API-Verbindung zu öffnen.

  2. Gespeicherte Verbindung im Verbindungspool unter dem Benutzernamen speichern.

  3. Wenn mein benutzerdefinierten IAuthorizationPolicy.Evaluate() aufgerufen wird, ich an der generischen Identität zur Verfügung gestellt aussehen:

    IIdentity GetClientIdentity(EvaluationContext evaluationContext) 
    { 
        object obj; 
        if (!evaluationContext.Properties.TryGetValue("Identities", out obj)) 
         throw new Exception("No Identity found"); 
    
         IList<IIdentity> identities = obj as IList<IIdentity>; 
         if (identities == null || identities.Count <= 0) 
          throw new Exception("No Identity found"); 
    
         return identities[0]; 
        } 
    

(leider kann ich nicht loswerden diese schlechten HTML Flucht)

  1. Ich nehme dann eine Verbindung aus dem Pool basierend auf dem IIdentity.Name, verwenden Sie diese Verbindung, um benutzerspezifische Daten aus der Datenbank zu laden und in einer benutzerdefinierten Identität und Principal zu speichern was ich im Evaluat einstelle ionContext:

    public bool Evaluate(EvaluationContext evaluationContext, ref object state) 
    { 
        IIdentity identity = GetClientIdentity(evaluationContext); 
        if (identity == null) 
         throw new Exception(); 
    
         // These are my custom Identity and Principal classes 
         Identity customIdentity = new Identity(); 
         Principal customPrincipal = new Principal(customIdentity); 
         // populate identity and principal as required 
         evaluationContext.Properties["Principal"] = customPrincipal; 
         return true; 
        } 
    

Dann sollte ich Zugriff auf meine individuelle Identität und Haupt, wenn ich es brauche von System.Threading.Thread.CurrentPrincipal oder CurrentIdentity verwenden.

Hoffe, das hilft in gewisser Weise; Ich bin mir nicht sicher, ob das der beste Weg ist, aber es ist das Beste, was mir bisher eingefallen ist ...

Steve

+0

Ich verwendete einen ähnlichen Ansatz, außer dass ich das Benutzerobjekt (IPrincipal) anstelle der Datenbankverbindung zwischenspeichern. Ich habe versucht, es im Validator auf Thread.CurrentPrincipal zu speichern, aber es wird mit einem GenericPrincipalin vor dem AuthorizationPolicy.Evaluate() -Aufruf überschrieben. AuthorizationPolicy.Evaluate wird aufgerufen, nachdem ich mein Benutzerobjekt bereits in den Passwort-Validator geladen habe. Ich denke, das war von WCF, den Designern, kurzsichtig. Die Grundidee ist also, wo im Thread wir es verstauen? Warum kann ich das nicht einfach im Validator machen und WCF lassen? >> Thread.CurrentPrincipal = Benutzer; – codenheim

Verwandte Themen