2010-02-19 15 views
5

Mam działający niestandardowy UserNamePasswordValidator, który wywołuje moją bazę danych Oracle.WCF Custom Validator: Jak zainicjować obiekt "User" z własnego walidatora

Ta klasa wywodzi się z metody System.IdentityModel.Selectors.UserNamePasswordValidator, a metoda Validate() zwraca wartość void.

Załaduję obiekt użytkownika z bazy danych, a po sprawdzeniu poprawności hasła chcę ukryć swój obiekt "Użytkownik", aby usługa mogła uzyskać do niego dostęp podczas wykonywania swojej działalności. W środowisku ASP.NET/Java zapisałbym go w sesji, a może w mojej ogólnej klasie kontrolera. Jak to zrobić z walidatora w WCF?

Lub, innymi słowy, jaka jest najlepsza praktyka w obszarze WCF, aby ustawić niestandardowy obiekt domeny użytkownika dla usługi.

Aktualizacja: Tak właśnie nad tym pracowałem. Przechowuję bufor obiektu User podczas walidatora, a następnie uzyskuję do niego dostęp później w kroku AuthorizatinPolicy.

// 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

Teraz ładuję mój obiekt użytkownika (IPrincipal) w walidatorze haseł, buforując go w statycznym słowniku, chwytając go w AuthoriziationPolicy. Zobacz edycję powyżej. Czy to najlepszy sposób? Na pewno wygląda jak kundel na tak bogate ramy. – codenheim

+0

Cóż, przykręciłem format w mojej edycji powyżej i nie mogę go naprawić. To miała być pełna metoda powyżej w obszarze kodu. – codenheim

Odpowiedz

4

Nie jestem ekspert WCF, ale z tego co czytałem i wdrożone do tej pory, „poprawny” sposób to zrobić byłoby użyć Validator do Uwierzytelnij użytkownik, a następnie zaimplementuj IAuthorizationPolicy, aby wykonać faktyczną autoryzację. Tak więc w polityce autoryzacji ustawisz niestandardową nazwę użytkownika w bieżącym wątku.

Aby można było przesyłać dalej informacje po sprawdzeniu poprawności nazwy użytkownika/hasła, można zaimplementować uwierzytelnianie tokenów zabezpieczeń, który dziedziczy po UserNameSecurityTokenAuthenticator. SecurityTokenAuthenticator najpierw wywoła weryfikator, a jeśli walidacja się powiedzie, może dodać niestandardową zasadę autoryzacji i wysłać informacje o profilu użytkownika do strategii za pośrednictwem konstruktora. Coś długiego w tej kwestii:

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(); 
    } 
} 

Tutaj jest artykuł opisujący nieco więcej zaangażowanych klas; http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx

+0

Przyjrzę się bliżej Twojej odpowiedzi i spróbuję, i zaktualizuję pytanie. Buforowanie użytkownika (głównego) na mapie natychmiast rozwiązało problem i nie miałem jeszcze czasu, aby ponownie zapoznać się z tą sekcją kodu, ale twoje jest pierwszym prawdopodobnym wyjaśnieniem, jakie widziałem. – codenheim

+0

Jak podłączyć to w rurociągu? Używasz również CustomUserNameValidator, czy też przechwytujesz go w pliku konfiguracyjnym. Jeśli tak, to czy nie jest on wywoływany dwa razy? Czy to podejście zwraca żądanie uwierzytelnienia do klienta (w przypadku użycia Basic Auth)? –

+0

BŁĄD: CustomUsernameSecurityTokenAuthenticator "nie implementuje dziedziczonego elementu abstrakcyjnego" System.IdentityModel.Selectors.UserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore (ciąg, ciąg) " – Nuzzolilo

1

Mam dokładnie ten sam problem.

Korzystam z interfejsu API do łączenia się z moją bazową bazą danych Oracle, a ja "sprawdzam" szczegóły logowania, otwierając połączenie.

Następnie chcę przechowywać to połączenie gdzieś (dość proste, utworzę pulę połączeń dla wszystkich różnych użytkowników), ale także utworzę niestandardową Tożsamość i Zleceniodawcę reprezentującą tego użytkownika, tak aby po uzyskaniu mojej niestandardowej IAuthorizationPolicy , nie trzeba ponownie ładować tych informacji.

Zrobiłem dużo poszukiwań i nie znalazłem nic więc mój plan jest, aby to zrobić:

  1. Sprawdź poprawność danych logowania w niestandardowych UserNamePasswordValidator otwierając połączenie API.

  2. Przechowuj otwarte połączenie w puli połączeń pod nazwą użytkownika.

  3. Kiedy mój zwyczaj IAuthorizationPolicy.Evaluate() jest wywoływana, będę patrzeć na tożsamości rodzajowej przewidzianej:

    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]; 
        } 
    

(przepraszam nie mogę pozbyć się tej biednej uciekającego HTML)

  1. Następnie pobieram połączenie z puli na podstawie nazwy IIdentity.Name, użyj tego połączenia, aby załadować dane użytkownika z bazy danych i zapisać je w niestandardowej Tożsamości i Zleceniodawcy które ustawiam w Ocenie 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; 
        } 
    

Następnie powinien mieć dostęp do mojego niestandardowego tożsamości i mocodawcy ilekroć muszę go za pomocą System.Threading.Thread.CurrentPrincipal lub CurrentIdentity.

Mam nadzieję, że to pomaga w pewien sposób; Nie jestem pewien, czy to najlepszy sposób, aby to osiągnąć, ale jest to najlepsze, co do tej pory wymyśliłem ...

Steve

+0

Użyłem podobnego podejścia z wyjątkiem buforowania obiektu użytkownika (IPrincipal) zamiast połączenia z bazą danych. Próbowałem zapisać go w wątku.CurrentPrincipal w walidatorze, ale zostanie on nadpisany przez GenericPrincipalin przed wywołaniem AuthorizationPolicy.Evaluate(). Wartość AuthorizationPolicy.Evaluate jest wywoływana po załadowaniu obiektu User do walidatora haseł. Myślę, że to było krótkowzroczne przez projektantów WCF. Więc podstawową ideą jest to, gdzie w Nicie to ukrywamy? Dlaczego nie mogę po prostu zrobić tego w walidatorze i mieć WCF zostawić to w spokoju? >> Thread.CurrentPrincipal = użytkownik; – codenheim

Powiązane problemy