Ir al contenido principal

Conectarse al LDAP (directorio activo) utilizando JAVA


Un LDAP es un sistema de autenticación estándar utilizado por muchas compañías para controlar el acceso a aplicaciones y recursos. Por lo general se espera que cualquier nueva aplicación haga uso del LDAP para realizar la autenticación y controlar los permisos en forma unificada, la seguridad es transversal a todos los procesos que realiza una organización.

Una vez regado el cuento a lo que vinimos, como conectarse a un LDAP. Lo primero que debes saber es que no se requieren librerías adicionales, JAVA en su distribución estandar ya cuenta con todo lo que necesitas.

primero tres siglas que tienes que tener en cuanta.
  • CN = Common Name
  • OU = Organizational Unit
  • DC = Domain Component
Para conectarse primero necesitas es instanciar un Objeto de la clase LdapContext, este se encargara de manejar la conexión al LDAP y las peticiones que se hagan al mismo. por consiguiente necesitara que le entregues una serie de propiedades de conexión. Esto lo haras con un Map de la siguiente forma:

Hashtable<String, String> env = new Hashtable<String, String>();
            env.put(Context.INITIAL_CONTEXT_FACTORY,
                    "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, "aquí va el nombre del usuario por lo general antecedido de un dominio pj EMPRESA//nombre_usuario");//esto te lo dice el administrador de LDAP
            env.put(Context.SECURITY_CREDENTIALS, "aqui va la contraseña del usuario");
            env.put(Context.PROVIDER_URL, "ldap://ip del servidor deLDAP:Puerto del servidorLDAP");//esto tambien te lo da el administrador del LDAP


Este Mapa se lo envías como argumento al constructor del LdapContext así:

ctx = new InitialLdapContext(env, null);

y listo ya con esto has confirmado que uno; te pudiste conectar al servidor de LDAP, dos el usuario existe y tres la contraseña es correcta. Si alguna de estas cosas falla la creación del contexto va a arrojar una  NamingException

Si no te arrojo esta Excepción ya estas conectado y puedes hacer consultas al LDAP, la primera y la mas básica es preguntar por los grupos a los que pertenece nuestro usuario, esto se hace así.

desde la instancia del LdapContext vas a llamar la función search(), esta función recibe tres argumentos:

1. un objeto de la clase SearchControls que se encarga entre otras cosas de fijar el alcance de las consultas en el LDAP. en nuestro caso y para mas o menos todos los casos sirve el mismo código.

SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

es decir busca en todos los subniveles.

2. un String con la pregunta que le vamos a hacer al LDAP (esto es lo mas complicado de entender pero mas o menos va así ), hay un paracentesis que encierra una serie de argumentos; después de abrir el paracentesis se puede poner una condición & para la condición AND o un | para la condición OR, después de la condición van los argumentos de la consulta entre paracentesis en nuestro caso vamos a preguntar por un objeto de la clase usuario que tenga un nombre de cuenta igual al nombre de cuenta de nuestro usuario, esto se ve así;

String searchFilter =  "(&(objectClass=user)(sAMAccountName=" + username + "))"; 

Si quisiera preguntar por los grupos que inician por la palabra Asociado se veria así

String searchFilter =  "(&(objectClass=group)(sAMAccountName=Asociado*))"; 

como vez se vale el * que significa cualquier cosa en el caso de arriba Asociado(seguido de cualquier cosa)

para mayor info mira este link https://confluence.atlassian.com/display/DEV/How+to+write+LDAP+search+filters

3. Una ves creada nuestra consulta, vamos a crear la base de la consulta, que es en palabras simples el directorio inicial sobre el que vamos a hacer las búsquedas para esto va un String así.

String ldapSearchBase = "DC=empresa,DC=com,DC=co";

de donde sacas esto??????????????????!!!!!!!!!!!
esto se lo preguntas al administrador del LDAP le preguntas literal por el CN, el te dará una cadena que se ve como la de arriba. pero con las siglas raras como OU tu ignora las demás y solo copia las que dicen DC.

Con los argumentos armados sigue hacer el llamado: que va así

NamingEnumeration<SearchResult> results = ctx.search(ldapSearchBase, searchFilter, searchControls);

Como ves esto retorna un Enumeración que contiene objetos de tipo SearchResult que sigue.. pues recorrer la enumeración esto se hace con un while en cada iteración almacenamos el serachResult en una variable para poder utilizarlo

 while(results.hasMoreElements()) {//mientras tenga mas elementos.
                 searchResult = (SearchResult) results.nextElement();//guarde el siguiente

Si quieres ver todos los atributos del usuario le haces así

NamingEnumeration<String> var = searchResult.getAttributes().getIDs();//pones todas las propiedaes en una variable para poder recorrerlas

String prop; 
                while(var.hasMoreElements()){
                prop=var.next();
                System.out.println(prop+" %"+searchResult.getAttributes().get(prop).get());
                 
                 }


Si quieres ver una propiedad en concreto como los grupos a los que pertenece el usuario memberOf (y esta es la propiedad que de verdad importa porque es la que se puede utilizar para verificar permisos) le haces así:

 Attribute memberOf = searchResult.getAttributes().get("memberOf");
                
                for (int i = 0; i < memberOf.size(); i++) 
                {
                    Attributes atts = ctx.getAttributes(memberOf.get(i)
                            .toString(), new String[] { "CN" });
                    Attribute att = atts.get("CN");
                    System.out.println(att.get().toString());
                }



cada CN es un grupo al que pertenece el usuario. y listo así se accede a un LDAP desde JAVA.

este seria todo el código:

    public static void main(String[] args) {
   
    LdapContext ctx = null;
    Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, "aquí va el nombre del usuario por lo general antecedido de un dominio pj EMPRESA//nombre_usuario");//esto te lo dice el administrados de LDAP
        env.put(Context.SECURITY_CREDENTIALS, "aqui va la contraseña del usuario");
        env.put(Context.PROVIDER_URL, "ldap://ip del servidor deLDAP:Puerto del servidorLDAP");//esto tambien te lo da el administrados del LDAP
        
        try {
ctx = new InitialLdapContext(env, null);

SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

String searchFilter =  "(&(objectClass=user)(sAMAccountName=nombreusuario))"; 
String ldapSearchBase = "DC=empresa,DC=com,DC=co";
NamingEnumeration<SearchResult> results = ctx.search(ldapSearchBase, searchFilter, searchControls);
SearchResult searchResult = null;
while(results.hasMoreElements()) {
                 searchResult = (SearchResult) results.nextElement();
                NamingEnumeration<String> var = searchResult.getAttributes().getIDs();
                String prop; 
                while(var.hasMoreElements()){
                prop=var.next();
                System.out.println(prop+" %"+searchResult.getAttributes().get(prop).get());
                 
                 }
                 
                
                Attribute memberOf = searchResult.getAttributes().get("memberOf");
                
                for (int i = 0; i < memberOf.size(); i++) 
                {
                    Attributes atts = ctx.getAttributes(memberOf.get(i)
                            .toString(), new String[] { "CN" });
                    Attribute att = atts.get("CN");
                    System.out.println(att.get().toString());
                }
                
                 System.out.println((String)searchResult.getAttributes().get("memberOf").get());
              
            }

} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
   
   
   
       // RetrieveUserAttributes retrieveUserAttributes = new RetrieveUserAttributes();
        //retrieveUserAttributes.getUserBasicAttributes("ncabrales", retrieveUserAttributes.getLdapContext());
       // retrieveUserAttributes.getLdapContext();
    }






Comentarios

Entradas populares de este blog

Clases anónimas JAVA (Anonymous Classes)

Código fuente articulo: https://www.dropbox.com/s/pzw44ot0ji2metl/Lambda.zip?dl=0 Las Clases anónimas en JAVA son una solución rápida para implementar una clase que se va utilizar una vez y de forma inmediata. Por ejemplo el  EventHandler  para un botón se puede implementar en la misma asignación valiendonos de la interfaz  EventHandler  que ya esta definida. Pero mejor vamos con un ejemplo mas simple. De la definición anterior concluimos dos cosas la primera es que para crear una clase anónima es necesario haber definido una interfaz, una clase o una clase abstracta. La clase anónima lo que hará sera implementar la interfaz definida o sobre escribir los métodos definidos. Para ilustrar esto utilizaremos el ejemplo del JAVA Tutorial https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html . en este ejemplo tenemos que implementar clases que cumpliendo con la interfaz Saludo sean capaces de saludar en diferentes idiomas. El paso uno ...

Paralelismo en JAVA Executors (ExecutorService, Callables y Futures).

Código fuente articulo: https://www.dropbox.com/s/jci67120hmd0uce/Paralelismo.zip?dl=0 Para manejo de concurrencia Java desde la versión 5 presento el Concurrency API  este presento una mejora substancial en el manejo de hilos y procesos en paralelo, antes solo contabas con Thread y Runnable. lo que te obligaba a controlar la creación y el numero de hilos de ejecución, no te entregaba un resultado del procesamiento y no te dejaba controlar las Excepciones que lanzara un hilo...un hilo se lanzaba y amenos que le enviaras un un CallBack perdías todo contacto con el. Para solucionar este problema se crearon dos tipos de objetos Callables y Futures. estos dos te permiten encapsular una tarea asignándole un tipo de Objeto que sera el valor de retorno y hacer seguimiento a las tareas que ejecuto en paralelo pudiendo preguntar si ya termino, que resultado lanzo y que excepciones ocurrieron. Arranquemos con la implementación de Callable, Callable es una interfaz que te permite de...