2010-02-19 18 views
5

Tengo un UserNamePasswordValidator personalizado que funciona y que llama a mi Oracle DB.Validador de WCF personalizado: cómo inicializar un objeto "Usuario" del validador personalizado

Esta clase se deriva de System.IdentityModel.Selectors.UserNamePasswordValidator y el método Validate() devuelve void.

Cardo mi objeto Usuario de la base de datos, y una vez que se valida la contraseña, quiero guardar mi objeto "Usuario" para que el servicio pueda acceder a él cuando se ocupa de sus asuntos. En ASP.NET/Java land lo guardaría en una sesión, o tal vez en mi clase general de Controller. ¿Cómo hago esto desde el Validator en WCF?

O, en otras palabras, ¿cuál es la mejor práctica en terrenos de WCF para establecer un objeto de dominio de usuario personalizado para el servicio.

Actualización: Así es como he trabajado para solucionarlo. Guardo en la memoria caché el objeto User durante el validador, luego lo accedo más adelante en el paso 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

Ahora estoy cargando mi objeto Usuario (IPrincipal) en el validador de contraseñas, guardándolo en caché en un Diccionario estático, tomándolo en la AuthoriziationPolicy. Ver editar arriba. Es esta la mejor manera? Claro parece un desafío para un marco tan rico. – codenheim

+0

Bueno, arruiné el formato en mi edición anterior y no puedo arreglarlo. Se suponía que era un método completo arriba en el área de código. – codenheim

Respuesta

4

No soy un experto en WCF, pero por lo que he leído y aplicado hasta ahora, la forma 'correcta' de hacer esto sería utilizar la Validator a autenticar al usuario, y luego implementar un IAuthorizationPolicy para hacer la autorización real. Por lo tanto, sería en la política de autorización que configuraría su principal personalizado en el hilo actual.

Para poder reenviar información de la validación de nombre de usuario/contraseña, puede implementar un autenticador de token de seguridad que hereda de UserNameSecurityTokenAuthenticator. El SecurityTokenAuthenticator primero llamará al validador y si la validación tiene éxito, puede agregar su política de autorización personalizada y enviar información de usuario a la política a través del constructor. Algo así como las líneas de esto:

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

Hay un artículo aquí que describe un poco más sobre las clases involucradas; http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx

+0

Voy a mirar más de cerca su respuesta y probarla, y actualizar la pregunta. El almacenamiento en caché del usuario (Principal) en un mapa resolvió el problema de inmediato, y no he tenido tiempo de revisar esa sección de código todavía, pero la tuya es la primera explicación plausible que he visto. – codenheim

+0

¿Cómo enlazar esto en la tubería? Además, usa CustomUserNameValidator, también lo enlaza en el archivo de configuración. Si es así, ¿no se llama dos veces? ¿Este enfoque devuelve una solicitud de autenticación al cliente (en caso de que se utilice autenticación básica)? –

+0

ERROR: CustomUsernameSecurityTokenAuthenticator 'no implementa el miembro abstracto heredado' System.IdentityModel.Selectors.UserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore (string, string) ' – Nuzzolilo

1

Tengo exactamente el mismo problema.

Estoy usando una API para conectarme a mi base de datos Oracle subyacente, y "valido" los datos de inicio de sesión abriendo una conexión.

Quiero guardar esta conexión en algún sitio (lo suficientemente fácil, crearé un grupo de conexiones para todos los diferentes usuarios), pero también crear una identidad personalizada y un principal que represente a este usuario, de modo que una vez que llegue a mi IAuthorizationPolicy , no necesita volver a cargar esta información.

he hecho un montón de búsqueda y no encontró nada, así que mi plan es hacer esto:

  1. Validar datos de acceso en UserNamePasswordValidator personalizada mediante la apertura de la conexión API.

  2. Almacenar la conexión abierta en el grupo de conexiones con el nombre de usuario.

  3. Cuando mi costumbre IAuthorizationPolicy.Evaluate() se llama, voy a mirar la identidad genérica dada:

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

(lo siento, no puedo deshacerme de este pobre HTML escape)

  1. Luego, obtengo una conexión del grupo basado en el nombre de IIdentity.Name, uso esta conexión para cargar datos específicos del usuario de la base de datos y almacenar esto en una identidad personalizada y principal que establecí en la Evaluación 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; 
        } 
    

Entonces debe tener acceso a mi identidad personalizada y principal cada vez que lo necesite mediante el uso de System.Threading.Thread.CurrentPrincipal o CurrentIdentity.

Espero que esto ayude de alguna manera; No estoy seguro de que sea la mejor manera de hacerlo, pero es lo mejor que he encontrado hasta ahora ...

Steve

+0

Utilicé un enfoque similar, excepto que guardo en caché el objeto Usuario (IPrincipal) en lugar de la conexión de la base de datos. Intenté almacenarlo en Thread.CurrentPrincipal en el validador, pero se sobrescribe con un GenericPrincipalin antes de la llamada AuthorizationPolicy.Evaluate(). Se llama a AuthorizationPolicy.Evaluate después de que ya cargué mi objeto User en el validador de contraseñas. Creo que esto fue miope por WCF los diseñadores. Entonces, ¿la idea básica es dónde en el hilo lo escondemos? ¿Por qué no puedo hacer esto en el validador y dejar que WCF lo deje en paz? >> Thread.CurrentPrincipal = usuario; – codenheim

Cuestiones relacionadas