Documente Academic
Documente Profesional
Documente Cultură
Copyright
Copyright (c) 2007, Irenio Luis Chagua Aduviri. Este documento puede ser distribuido solo bajo los términos y
condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la última versión se encuentra en
http://www.javahispano.org/licencias/).
Para cualquier comentario, duda, consulta sobre este tutorial dirigirse a ichagua@nspsac.com.
Contenido del Artículo
Introducción ............................................................................................ 1
1. LDAP (Lightweight Directory Access Protocol) ............................. 3
Utilidades de LDAP ............................................................................... 3
Características de LDAP ....................................................................... 4
Estructura del Arbol LDAP ................................................................... 4
Entradas.............................................................................................. 4
Atributos............................................................................................. 4
Tipos de Atributos ............................................................................. 5
Clase de Objetos ................................................................................ 6
LDIF .................................................................................................... 6
Modelo de Nombres LDAP ................................................................ 6
2. Directorio LDAP............................................................................... 8
3. Browser LDAP ............................................................................... 14
4. Librería para acceder a LDAP ....................................................... 14
API JNDI............................................................................................... 15
Configuración de Recursos JNDI ........................................................ 16
5. Creando las clases JavaBean......................................................... 18
6. Implementando las clases DAO ..................................................... 21
La interfaz DAO ................................................................................... 21
La clase DAO Factory ......................................................................... 22
Y la implementación de la interfaz DAO ............................................. 22
7. Directorio SDK para Java............................................................... 24
Búsqueda de una Entrada de Directorio............................................. 24
Autenticando Usuarios ........................................................................ 28
Creando y Manipulando entradas de directorio ................................. 29
Servlets, JSP y LDAP .......................................................................... 33
8. Directorios LDAP ........................................................................... 37
9. Clientes LDAP ................................................................................ 37
10. Librerías para LDAP ................................................................... 37
11. Referencias y Herramientas utilizadas....................................... 37
Introducción
Cuando se está desarrollando aplicaciones empresariales, muchas veces se tiene un
directorio de personas, objetos, que son necesarios tenerlos ordenado en un formato
estándar, uno de ellos es LDAP (Lightweight Directory Access Protocol), pero las
formas de acceso a este directorio son diversas, por eso se ha pensado en
implementar el acceso desde un directorio SDK para Java utilizando un servidor web
Apache Tomcat. El directorio LDAP utilizado es OpenLDAP, uno de los directorios
que está al alcance del mundo del software libre.
En este artículo se presenta el uso del directorio LDAP con la finalidad de conocer
con mayor profundidad, creando nuevas especificaciones de esquemas del directorio
y nuevas clases de objetos con sus respectivos atributos. El mismo directorio es
posible mostrar mediante un Browser LDAP en modo gráfico.
1
1. LDAP (Lightweight Directory Access
Protocol)
LDAP (Lightweight Directory Access Protocol) o Protocolo de Acceso Ligero a
Directorio es un protocolo de comunicación que permite acceder y modificar
información almacenada en un directorio ordenado y distribuido en forma jerárquica,
actualmente está en la versión 3. LDAP permite almacenar información de cuentas de
usuario, contactos, ubicación de diversos recursos de la red, permisos de usuarios,
certificados, entre otros; éste directorio está optimizado para acceso de lectura en
forma eficiente y almacenar datos de poco tamaño, las modificaciones se presentan con
poca frecuencia como el caso de un correo electrónico.
Utilidades de LDAP
Directorios de información, por ejemplo datos de empleados organizados por
departamentos (siguiendo la estructura organizativa de la empresa) ó cualquier tipo
de páginas amarillas.
Sistemas de autenticación/autorización centralizada, sistemas donde se guarda gran
cantidad de registros y se requiere un uso constante de los mismos, por ejemplo,
gestión de cuentas de acceso a una red corporativa, sistema de autenticación para
páginas web, sistemas de control de entradas a edificios, oficinas, entre otros.
Sistemas de correo electrónico.
Sistemas de alojamiento de páginas web y FTP.
Sistemas de autenticación basados en RADIUS, para el control de accesos de los
usuarios a una red de conexión o ISP.
Servidores de certificados públicos y llaves de seguridad.
Autenticación única ó “single sign-on” para la personalización de aplicaciones.
Perfiles de usuarios centralizados, para permitir itinerancia ó “Roaming”.
Libretas de direcciones compartidas.
3
LDAP (Lightweight Directory Access Protocol)
Características de LDAP
Operaciones de lectura muy rápidas. Debido a la naturaleza de los datos
almacenados en los directorios las lecturas son más comunes que las escrituras.
Datos relativamente estáticos. Los datos almacenados en los directorios no suelen
actualizarse con mucha frecuencia.
Entorno distribuido, fácil replicación.
Estructura jerárquica. Los directorios almacenan la información de forma jerárquica
de forma nativa.
Orientadas a objetos. El directorio representa a elementos y a objetos. Los objetos
son creados como entradas, que representan a un conjunto de atributos.
Esquema estándar. Los directorios utilizan un sistema estándar que pueden usar
fácilmente diversas aplicaciones.
Atributos multi-valor. Los atributos pueden almacenar un valor único o varios.
Replicación multi-master. Muchos de los servidores LDAP permiten que se realicen
escrituras o actualizaciones en múltiples servidores.
Entradas
El modelo de información de LDAP está basado en entradas. Una entrada es un conjunto
de atributos que tienen un único Nombre Distintivo DN (Distinguished Name) y de forma
global. El DN se utiliza para referirse a una entrada sin ambigüedades. Cada atributo de
una entrada tiene un tipo y está asociado uno o más valores. Los tipos son normalmente
palabras nemotécnicas, como "cn" para common name, o "mail" para una dirección de
correo electrónico.
La sintaxis de los atributos depende del tipo de atributo. Por ejemplo, un atributo cn
puede tener el valor “Mariano Apaza Quispe”. Un atributo mail puede contener un valor
“mapaza@nspsac.com”. El atributo jpegPhoto contiene una fotografía en formato JPEG.
Atributos
Los datos del directorio son un conjunto de atributos y sus respectivos valores. Por
ejemplo el atributo commonName, o cn, se usa para representar el nombre de una persona.
Tipos de Atributos
Una definición de tipo de atributo especifica la sintaxis de un atributo y cómo se
ordenan y comparan los atributos de ese tipo.
Los tipos de atributos en el directorio forman un árbol de clases. Por ejemplo, el tipo de
atributo "commonName" es una subclase del tipo de atributo "name". Hay atributos
obligatorios y opcionales que se muestran en la Tabla 1.1.
Clase de Objetos
En LDAP, una clase de objetos define el conjunto de atributos a ser usados para definir
una entrada. El estándar LDAP proporciona estos tipos básicos para las clases de
objetos:
Una entrada determinada puede pertenecer a más de una clase de objetos. Por ejemplo,
la entrada para personas se define mediante la clase de objetos person, pero también
puede definirse mediante atributos en las clases de objetos inetOrgPerson, groupOfNames
y organization. La estructura de clases de objetos del servidor determina la lista total
de atributos requeridos y permitidos para una entrada concreta.
LDIF
El formato de intercambio de LDAP es un archivo de texto que almacena información de
entradas de objetos en forma jerárquica. Esto nos permite importar y exportar
información de directorio entre servidores de directorios basados en LDAP.
dn: uid=mapaza,ou=People,dc=nspsac,dc=com
givenName: Mariano
sn: Apaza Quispe
mail: mapaza@nspsac.com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: contribuyente
uid: mapaza
cn: Mariano Apaza Quispe
dni: 01234567
ruc: 10012345675
employeeNumber: A214
manager: uid=jperez,ou=Admin,dc=nspsac,dc=com
telephoneNumber: 051505050
mobile: 0519797979
direccion: Jr. Los Incas Nro. 6548
st: Puno
l: Ilave
userPassword: abc
jpegPhoto:<file:///var/photo.jpg
2. Directorio LDAP
Para almacenar estas descripciones de directorios es necesario el uso de un directorio
LDAP, hay varias implementaciones de estos directorios, de distintas empresas, para
distintos usos y para aplicaciones diferentes. Para el caso del presente artículo haremos
uso de OpenLDAP, un directorio LDAP a nuestro alcance en el mundo del software
libre.
database bdb
suffix "dc=nspsac,dc=com"
rootdn "cn=root,dc=nspsac,dc=com"
rootpw abc
directory ./data
rootpw {SSHA}/Wg8V59/aoeKLn4PkkKWEsdvjyz6R+/E
include ./schema/core.schema
include ./schema/cosine.schema
include ./schema/inetorgperson.schema
En una especificación de esquema se definen clases de objetos válidos que indican qué
atributos debe contener en forma obligatoria y qué atributos son opcionales, así como el
tipo de dato (cadenas de texto, números) de un atributo. La clase de objetos y los
atributos deben estar definidos en forma global y único mediante cadenas de números
(OID), para esto es necesario obtener un OID que nos permitirá crear tantas
extensiones como se quiera del esquema.
Los tipos de clase de objetos que existen son tres: Structural. Una clase de objeto
estructural define las características básicas de un objeto. Auxiliary. Una clase de
objeto Auxiliar es adicional, complementa los atributos de una clase de objeto
estructural. Y por último Abstract. Esta clase de objeto abstracto es usado solamente
para definir modelo de datos LDAP básicos.
Donde:
1.1.2.2.2 es el identificador único global (OID).
NAME 'myPerson' es el nombre de la clase de objeto (alias para OID).
DESC 'Mi persona' la descripción para la clase de objeto.
SUP inetOrgPerson es el objeto del que hereda.
MUST(...) aquí se describen los atributos requeridos.
MAY(...) aquí se describen los atributos opcionales.
Donde:
1.3.6.1.1.1.1.0 es el identificador único global (OID).
NAME 'uidNumber' es el nombre del atributo (alias para OID).
DESC '...' la descripción para el atributo.
EQUALITY integerMatch calificador de plantilla de tipos.
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 es la sintaxis OID.
SINGLE-VALUE es un calificador, puede ser SINGLE-VALUE, COLLECTIVE y {LENGTH}.
#
# OID prefix: 1.3.6.1.4.1.10018
#
# Attributes: 1.3.6.1.4.1.10018.1.1
#
#
# Objects: 1.3.6.1.4.1.10018.1.2
#
include ./schema/contribuyente.schema
dn: ou=Admin,dc=nspsac,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Admin
dn: ou=People,dc=nspsac,dc=com
objectClass: top
objectClass: organizationalUnit
ou: People
dn: ou=Developer,dc=nspsac,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Developer
De otra forma también es posible insertar los datos de entrada del directorio con el
siguiente comando.
Y así añadimos otras entradas de directorio LDAP, pero ahora la contraseña del usuario
será encriptado con {SSHA} y es de la siguiente manera.
dn: uid=jmamani,ou=People,dc=nspsac,dc=com
givenName: Juan Antonio
sn: Mamani Choque
mail: jmamani@nspsac.com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: contribuyente
uid: jmamani
cn: Juan Antonio Mamani Choque
dni: 01847569
ruc: 10018475691
employeeNumber: 2097
mobile: 0519535353
direccion: Av. Titicaca Nro. 458
st: Puno
l: Juli
userPassword: {SSHA}IabIHNeVpLkbzDxCANEMj47OJ9QRh9Gj
Para generar una cadena de texto encriptada se hace uso del comando slappasswd que
trae OpenLDAP de la siguiente manera:
Como se ha visto se ha tenido que agregar en tres veces las entradas de directorio
LDAP definidos, pero no es necesario realizar para cada entrada de directorio, es
posible juntar las tres entradas de directorio en un solo formato LDIF denominado
nspsac.ldif.
Luego de haber insertado datos de entrada de directorio podemos hacer uso del
comando slapcat para mostrar todas las entradas de directorio que se encuentra en
nuestro directorio LDAP en formato LDIF, esto mismo nos puede servir para guardar
estas entradas para lo que sea necesario.
ldapsearch -x -b "uid=jmamani,ou=People,dc=nspsac,dc=com"
3. Browser LDAP
Hasta aquí hemos visto las operaciones de entradas de directorio OpenLDAP desde
líneas de comando, se puede utilizar en modo gráfico mediante un navegador de LDAP,
para esto utilizaremos LDAPBrowser que está basado en Java y ejecutamos la shell
lbe.bat y configuramos como la que se muestra en la figura. Con esto es posible
administrar un directorio LDAP en modo gráfico.
Los archivos generados, una vez compilado el código fuente, específicamente del
directorio packages, es necesario que los archivos JAR sean agregados a la variable de
En Linux
CLASSPATH=<LDAPSDKHOME>/packages/ldapjdk.jar:<LDAPSDKHOME>/
packages/ldapsp.jar:$CLASSPATH
export CLASSPATH
En FreeBSD, Unix
setenv CLASSPATH <LDAPSDKHOME>/packages/ldapjdk.jar:<LDAPSDKHOME>/
packages/ldapsp.jar:$CLASSPATH
En Windows
set CLASSPATH=<LDAPSDKHOME>/packages/ldapjdk.jar;<LDAPSDKHOME>/
packages/ldapsp.jar;%CLASSPATH%
API JNDI
Mediante el API de JNDI es posible escribir cualquier tipo de programa para acceder a
información en directorios LDAP, gestores de Base de datos relacionales, servicios
CORBA (COS, Corba Object Service), NDS de Novell, entre otras aplicaciones. Para que
un programa de Java busque información de cualquier tipo en un directorio LDAP debe
indicarse dentro del programa la ubicación del directorio LDAP mediante un Naming
Manager para la ubicación física del sistema. Esto es importante ya que en cualquier
momento es posible cambiar el servidor físico del directorio LDAP y no será necesario
la modificación de programa fuente para luego compilarlo, sino basta con cambiar los
parámetros de configuración.
Para este caso se creará una aplicación web con una estructura de directorios básico, lo
nombraremos ldap que se encontrará en el directorio $CATALINA_HOME/webapps. Luego
es necesario incluir un descriptor de la aplicación que es el archivo web.xml que
contendrá la configuración de la aplicación web y estará dentro del directorio WEB-INF/
del directorio raíz de la aplicación creada, es decir $CATALINA_HOME/webapps/ldap/WEB-
INF/web.xml.
Los recursos definidos en estos elementos pueden estar referidos por los siguientes
elementos al utilizar la descripción de una aplicación web que se encuentra en /WEB-
INF/web.xml:
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<resource-env-ref>
<description>
Objeto Factory para una instancia de BeanLDAPHost.
</description>
<resource-env-ref-name>
beanldaphost
</resource-env-ref-name>
<resource-env-ref-type>
com.nspsac.bean.BeanLDAPHost
</resource-env-ref-type>
</resource-env-ref>
</web-app>
Cada recurso JNDI disponible es configurado en base a una inclusión de los siguientes
elementos dentro de un elemento <Context> ó un elemento <DefaultContext>:
package com.nspsac.bean;
import java.io.Serializable;
import java.util.Hashtable;
Para escribir la clase Resource Factory se debe implementar la interfaz del proveedor
de servicio JNDI javax.naming.spi.ObjectFactory. Cada vez que la aplicación web
llama al método lookup() en una entrada del contexto que está asociado a este
Factory, el método getObjectInstance() es invocado con los siguientes argumentos:
package com.nspsac.bean;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
while (addrs.hasMoreElements()) {
RefAddr addr = (RefAddr) addrs.nextElement();
String nombre = addr.getType();
String value = (String) addr.getContent();
if (nombre.equals("iphost")) {
iphost = value;
} else if (nombre.equals("puerto")) {
try {
puerto = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new NamingException("Invalid 'port' value " + value);
}
} else if(nombre.equals("dnbase")){
dnbase = value;
} else if(nombre.equals("dnmgr")){
dnmgr = value;
} else if(nombre.equals("dnpwd")){
dnpwd = value;
} else if(nombre.equals("dnraiz")){
dnraiz = value;
} else if(nombre.equals("ctxfactory")){
ctxfactory = value;
}
}
return (bean);
}
}
Para compilar las clases JavaBean que hemos creado es necesario que la librería que
estamos utilizando del proveedor de servicios de interfaz de Netscape Directory SDK
para Java ldapsp.jar esté incluido dentro del directorio de librerías de Tomcat que es
$CATALINA_HOME/common/lib. Adicionalmente la librería SDK ldapjdk.jar también deberá
estar incluido en este directorio de librerías de Tomcat para que funcione nuestros
ejemplos de este artículo.
La interfaz DAO
package com.nspsac.seguridad.ldap;
import java.util.HashMap;
import javax.naming.NamingException;
import com.nspsac.utils.exception.IncompleteConversationalState;
import javax.naming.NamingException;
import com.nspsac.seguridad.ldap.AccesoLDAP;
import com.nspsac.seguridad.ldap.AccesoLDAPImpl;
package com.nspsac.seguridad.ldap;
import java.io.*;
import java.util.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import netscape.ldap.LDAPAttribute;
import netscape.ldap.LDAPAttributeSet;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
import netscape.ldap.LDAPSearchConstraints;
import netscape.ldap.LDAPSearchResults;
import netscape.ldap.LDAPv3;
import com.nspsac.bean.BeanLDAPHost;
import com.nspsac.utils.exception.IncompleteConversationalState;
htEnv.put("DN_HOST", beanldaphost.getAttribute("ldap_iphost"));
htEnv.put("DN_PORT", beanldaphost.getAttribute("ldap_puerto"));
htEnv.put("DN_HOST_PORT", host_port);
htEnv.put("DN_MGR", beanldaphost.getAttribute("ldap_dnmgr")+","
+beanldaphost.getAttribute("ldap_dnbase"));
htEnv.put("DN_PWD", beanldaphost.getAttribute("ldap_dnpwd"));
htEnv.put("DN_BASE", beanldaphost.getAttribute("ldap_dnbase"));
htEnv.put("DN_RAIZ", beanldaphost.getAttribute("ldap_dnraiz"));
htEnv.put("DN_CTX", beanldaphost.getAttribute("ldap_ctxfactory"));
ctx.close();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
if(initCtx != null)
initCtx.close();
}
return htEnv;
}
}
Nombre de Servidor donde está instalado el directorio LDAP, también puede ser la
dirección IP del servidor, se usa "localhost" cuando se realizan las pruebas en una
sola máquina.
Número de puerto del directorio LDAP, que es el puerto TCP de la máquina donde el
servidor de directorio es escuchado por las conexiones LDAP, el puerto estándar
para LDAP es 389 para las conexiones no SSL. Para las conexiones basados en SSL
es el puerto 636.
DN base del árbol de directorio administrado por el servidor, es el nombre base
como raíz por donde empezará a realizar la búsqueda, por ejemplo
ou=People,dc=nspsac,dc=com.
Alcance de la búsqueda (Scope), es el punto de partida de una búsqueda y la
profundidad a la que realiza la búsqueda en un árbol de directorio, hay tres opciones
para este alcance:
o BASE, representado por la constante LDAPConnection.SCOPE_BASE, es usado
solo para búsquedas de DN base.
o ONE, representado por la constante LDAPConnection.SCOPE_ONE, es usado para
indicar que realice la búsqueda de todas las entradas debajo de DN base,
pero no incluye el DN base.
Filtros de búsqueda, es la consulta que se realiza, es usado para filtrar las entradas
de directorios y devolver cierta cantidad de registros. Los filtros son usados
mediante los paréntesis y combinaciones de los símbolos ‘&’, ‘|’ y ‘!’ que
representan ‘And’, ‘Or’ y ‘Not’ respectivamente. Si se quiere ubicar a todas las
personas que sus apellidos (representado mediante el atributo sn) empiecen con
“Mamani" se realiza mediante el siguiente filtro:
(&(objectclass=person)(sn=Mamani*))
Los atributos que se quieren mostrar en la búsqueda, puede que a veces solo se
requiere algunos atributos y no todos los atributos presentes en una entrada de
directorio. Si no se quiere recuperar ningún atributo se puede usar la constante
LDAPConnection.NO_ATTRS. Si se desea recibir todos los atributos del usuario se
puede usar la constante LDAPConnection.ALL_USER_ATTRS, ó en otro caso puede usar
un array de cadenas de caracteres indicando los atributos que se quiere recuperar.
Opcionalemente las preferencias de búsqueda, estas preferencias incluyen la
cantidad de tiempo que se desea permitir para la búsqueda, máximo número de
registros que se aceptará, y si la búsqueda debe esperar hasta que todos los datos
son recibidos. Las preferencias de búsqueda son especificados usando la clase
LDAPSearchConstraints. Los métodos usados en esta clase son las siguientes:
o setBatchSize especifica cómo deben ser devueltos los resultados de la
búsqueda. El valor cero ‘0’ indica que debe esperar hasta que todos los
resultados sean devueltos, el valor uno ‘1’ devuelve cada resultado como
esté disponible.
o setHopLimit especifica cuántas veces deben ser devueltas en una búsqueda
de una entrada.
o setMaxResults especifica el número máximo de resultados que deben ser
devueltas desde una búsqueda. Se usa el valor cero ‘0’ para resultados
ilimitados.
o setReferrals especifica si el SDK debe ó no seguir las referencias
automáticamente.
o setServerTimeLimit especifica el número máximo de segundos que debe
tardar en entregar los resultados de la búsqueda.
Una vez repasado sobre los conceptos para el acceso a un directorio LDAP, ahora
definimos el método que realizará la búsqueda en un directorio mediante un filtro que le
pasaremos como parámetro, este filtro será de tipo “atributo=valor”, que nos puede
servir para realizar búsquedas por los atributos uid, employeeNumber, ruc, dni, mail,
cn, sn y de los demás atributos, siempre y cuando el atributo sea de tipo cadena de
texto. El método tiene el nombre de buscarEnLDAP(String uid).
Para la búsqueda, ya contamos con información del nombre del servidor, el puerto del
servidor, el DN base, los atributos que están definidos en la variable ATTRS, y las
preferencias de búsqueda se ha establecido a un número máximo de registro igual a 1,
ya que la búsqueda está pensado para los atributos como nombres distintivos relativos y
Los resultados de una búsqueda son devueltos como un objeto LDAPSearchResults. Hay
dos métodos para realizar el recorrido: nextElement y next, ambos métodos devuelven
un objeto que puede ser LDAPEntry, LDAPReferralException ó LDAPException. Para este
caso usaremos el método next.
Una vez que se tiene los atributos de una entrada, se puede obtener los valores de
estos atributos. La clase LDAPAttribute tiene varios métodos obtener el valor de los
atributos. Los métodos que normalmente se utilizan en la mayoría de los casos son los
siguientes:
try {
ldap.connect(3, (String)htEnv.get("DN_HOST"),
Integer.parseInt((String)htEnv.get("DN_PORT")),
(String)htEnv.get("DN_MGR"), (String)htEnv.get("DN_PWD"));
if (myResults.hasMoreElements()) {
LDAPEntry myEntry = myResults.next();
hMap.put("dn", myEntry.getDN());
while (attrsInSet.hasMoreElements()) {
LDAPAttribute nextAttr = (LDAPAttribute) attrsInSet.nextElement();
String attrName = nextAttr.getName();
if (attrName.trim().equalsIgnoreCase("jpegPhoto")) {
Enumeration valsInAttr = nextAttr.getByteValues();
if (valsInAttr.hasMoreElements()) {
if(savePhoto(namePhoto, (byte[])valsInAttr.nextElement())){
hMap.put(attrName.trim(), namePhoto);
}
else {
hMap = new HashMap();
hMap.put("Mensaje","Error al recuperar Foto");
break;
}
}
}
else{
Enumeration valsInAttr = nextAttr.getStringValues();
if (valsInAttr.hasMoreElements()) {
hMap.put(attrName.trim(), valsInAttr.nextElement());
}
}
}
}
else{
hMap.put("Mensaje","El Usuario indicado no se encuentra registrado.");
}
}
catch (LDAPException e) {
e.printStackTrace();
hMap.put("Mensaje","ERROR:"
+e.getLDAPResultCode()
+"->"
+LDAPException.errorCodeToString(e.getLDAPResultCode()));
}
catch (NumberFormatException e) {
e.printStackTrace();
hMap.put("Mensaje",
"HA OCURRIDO UN PROBLEMA EN LOS PARAMETROS DEL SERVIDOR:"
+ e.getMessage());
}
finally {
try {
if (ldap.isConnected()) {
ldap.disconnect();
}
}
catch (LDAPException ex) {
throw new IncompleteConversationalState(
"NO SE PUDO CERRAR EL ENLACE:"
+ ex.getLDAPResultCode()
+ "->"
+ LDAPException.errorCodeToString(ex.getLDAPResultCode()));
}
}
return hMap;
}
Autenticando Usuarios
Hasta aquí no hemos visto el tema de autenticación a un directorio LDAP. Las
conexiones hasta aquí han sido utilizando la cuenta por defecto configurado en el mismo
directorio LDAP, más no hemos utilizado la cuenta de usuario y su contraseña. Por
ejemplo es posible que en un directorio se quiera restringir el acceso a ciertos
atributos, no permitiendo el acceso a algunos atributos como la fotografía de un
empleado, que solamente puede tener acceso personal autorizado. Para realizar
cambios tales como agregar, modificar, consultar ó eliminar ciertos atributos de una
entrada de directorio LDAP, por lo general se debe autenticar.
El protocolo LDAP proporciona una operación para permitir conectar a los clientes para
autenticar al servidor. El método más simple de autenticación soportado por el
protocolo es un método que al cliente le permite enviar un DN y contraseña al servidor.
Para usar el método de autenticación simple, se puede usar mediante el método
LDAPConnection.authenticate ó un método LDAPConnection.connect variante que toma una
autenticación pasando como parámetros a DN y contraseña. Algunas de las excepciones
que se pueden producir son las siguientes:
try {
ldap.connect(3, (String)htEnv.get("DN_HOST"),
Integer.parseInt((String)htEnv.get("DN_PORT")),
(String)htEnv.get("DN_MGR"),
(String)htEnv.get("DN_PWD"));
}
if(hMap.get("Mensaje") == null){
hMap.put("Mensaje","Usuario ".concat(uid).concat(" autenticado"));
}
}
catch (LDAPException e) {
e.printStackTrace();
hMap = new HashMap();
switch (e.getLDAPResultCode()) {
case LDAPException.NO_SUCH_OBJECT:
hMap.put("Mensaje",uid.concat(": El usuario indicado no existe"));
break;
case LDAPException.INVALID_CREDENTIALS:
hMap.put("Mensaje",uid.concat(": Password invalido"));
break;
default:
hMap.put("Mensaje",uid.concat(
": No se ha podido realizar la autenticacion, error:"
+ e.getLDAPResultCode()));
break;
}
}
catch (NumberFormatException e) {
e.printStackTrace();
hMap = new HashMap();
hMap.put("Mensaje",
"HA OCURRIDO UN PROBLEMA EN LOS PARAMETROS DEL SERVIDOR:"
+ e.getMessage());
}
finally {
try {
if (ldap.isConnected()) {
ldap.disconnect();
}
}
catch (LDAPException ex) {
throw new IncompleteConversationalState(
"NO SE PUDO CERRAR EL ENLACE:"
+ ex.getLDAPResultCode()
+ "->"
+ LDAPException.errorCodeToString(ex.getLDAPResultCode()) + "<br>"
+ ex.getMessage());
}
}
return hMap;
}
Para agregar una nueva entrada de directorio es necesario definir un nombre distintivo
DN para la entrada y los atributos para esta entrada. Para agregar una nueva entrada es
necesario tener en cuenta los siguientes pasos:
Con estos pasos se ha creado el método addEntry que recibe dos parámetros, el primero
es el nombre distintivo identificador de usuario y el segundo un conjunto de atributos en
un Collection HashMap.
La otra operación con las entradas es la modificación de sus atributos. Para modificar
un atributo de una entrada, se ha utilizado el método modifyAttributes del objeto
DirContext, también es posible modificar un atributo con el uso del objeto
LDAPModification y luego invocando al método modify de LDAPConnection. Para modificar
los atributos de una entrada es necesario tener en cuenta los siguientes pasos:
nspsacCtx.modifyAttributes(dn, mods);
}
nspsacCtx.modifyAttributes(dn, mods);
}
nspsacCtx.modifyAttributes(dn, mods);
}
Los datos de una entrada deben estar bien guardados en un directorio LDAP. Pero llega
un momento en que será necesario borrar una entrada LDAP. Para esto quitar una
entrada de un directorio es muy simple. Solamente se debe especificar qué DN se debe
quitar e invocar al método LDAPConnection.delete.
ldap.delete(dn);
}
catch (LDAPException e) {
e.printStackTrace();
}
}
No solamente es posible cambiar el RDN en un mismo nivel del árbol del directorio
LDAP, hay la posibilidad de mover ó copiar a un RDN a una parte diferente del árbol del
directorio LDAP. Como ejemplo veamos al RDN uid=jmamani del DN
ou=People,dc=nspsac,dc=com lo movemos ó lo copiamos a otra parte del árbol como RDN
uid= juan.mamani y DN ou=Admin,dc=nspsac,dc=com.
package com.nspsac.seguridad;
import java.util.HashMap;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.nspsac.seguridad.ldap.AccesoLDAP;
import com.nspsac.seguridad.ldap.AccesoLDAPFactory;
import com.nspsac.utils.exception.IncompleteConversationalState;
try{
String accion = request.getParameter("accion");
String opcion = request.getParameter("modo");
AccesoLDAP acceso = AccesoLDAPFactory.create();
acceso.getInstance();
String usuario = request.getParameter("user");
if(accion.equals("autenticacion")){
String clave = request.getParameter("pwd");
hmAut = acceso.autenticar(usuario,clave);
session.setAttribute("hmAut", hmAut);
session.removeAttribute("hmSearch");
session.removeAttribute("cambioClave");
}
else if(accion.equals("busqueda")){
hmSearch = acceso.findByUID(usuario);
session.setAttribute("hmSearch", hmSearch);
session.removeAttribute("hmAut");
session.removeAttribute("cambioClave");
}
else if(accion.equals("cambioclave")){
String claveanterior = request.getParameter("oldpwd");
String clavenueva = request.getParameter("newpwd");
String claveconfirma = request.getParameter("repwd");
cambioClave = usuario.concat(": Usuario indicado no existe.");
if(clavenueva.equals(claveconfirma)){
acceso.cambiarClave(usuario,claveanterior,clavenueva);
cambioClave = usuario.concat(": La clave ha sido clambiada
satisfactoriamente.");
}
else{
cambioClave = usuario.concat(": Clave nueva y su confirmación
no concuerdan."+
"<br>Ingresar nuevamente las claves");
}
session.setAttribute("cambioClave", cambioClave);
session.removeAttribute("hmAut");
session.removeAttribute("hmSearch");
}
session.setAttribute("opcion", opcion);
try{
getServletConfig().getServletContext().getRequestDispatcher("/ldap").forward(req
uest, response);
}
catch(Exception e){
e.printStackTrace();
}
}
}
La página JSP que recoge valores enviados desde un servlet como los atributos de una
entrada de directorio, son definidos de la siguiente forma.
<%
HashMap hmAut = (HashMap)session.getAttribute("hmAut");
HashMap hmSearch = (HashMap)session.getAttribute("hmSearch");
String cambioClave = (String)session.getAttribute("cambioClave");
String opcion = (String)session.getAttribute("opcion");
%>
<%//... Para mostrar los datos de una entrada después de autenticar...%>
<%if(hmAut != null){
String uid = (String)hmAut.get("uid");
String sn = (String)hmAut.get("sn");
String givenName = (String)hmAut.get("givenName");
String cn = (String)hmAut.get("cn");
String employeeNumber = (String)hmAut.get("employeeNumber");
String dni = (String)hmAut.get("dni");
String ruc = (String)hmAut.get("ruc");
String direccion = (String)hmAut.get("direccion");
String mail = (String)hmAut.get("mail");
String telephoneNumber = (String)hmAut.get("telephoneNumber");
String mobile = (String)hmAut.get("mobile");
String jpegPhoto = (String)hmAut.get("jpegPhoto");
String msg = (String)hmAut.get("Mensaje");
if(msg != null){%>
<div class="titulo"><%=msg%></div>
<%}%>
<table>
<tr><td><b>Cuenta de Usuario :</b></td> <td><%=uid%></td></tr>
<tr><td><b>Apellidos :</b></td> <td><%=sn%></td></tr>
<tr><td><b>Nombres :</b></td> <td><%=givenName%></td></tr>
<tr><td><b>Nombre Completo :</b></td> <td><%=cn%></td></tr>
<tr><td><b>Registro Empleado :</b></td>
<td><%=(employeeNumber!=null?employeeNumber:"-")%></td></tr>
<tr><td><b>DNI :</b></td> <td><%=dni%></td></tr>
<tr><td><b>RUC :</b></td> <td><%=ruc%></td></tr>
<tr><td><b>Dirección :</b></td>
<td><%=direccion%> - <%=hmAut.get("l")%>,
<%=hmAut.get("st")%></td></tr>
<tr><td><b>Correo Electrónico :</b></td> <td><%=mail%></td></tr>
<tr><td><b>Teléfono :</b></td>
<td><%=(telephoneNumber!=null)?telephoneNumber:"-"%></td></tr>
<tr><td><b>Teléfono Móvil :</b></td> <td><%=(mobile!=null)?mobile:"-
"%></td></tr>
<tr>
<td valign="top"><b>Foto :</b></td>
<td>
<%if(jpegPhoto != null) {%>
<img src="imgs/<%=jpegPhoto%>" alt="" border="0" width="70"
height="80">
<% } else {%>
-
<% }%>
</td>
</tr>
</table>
<%}%>
<%//... Para mostrar datos de una entrada después de una búsqueda... %>
<%if(hmSearch != null){
String msg = (String)hmSearch.get("Mensaje");
if(msg != null){%>
<div class="titulo"><%=msg%></div>
<%
hmSearch.remove("Mensaje");
}
for (Iterator it = hmSearch.keySet().iterator(); it.hasNext();){
String name = (String)it.next();
String value = (String)hmSearch.get(name);
if(!name.equals("jpegPhoto")){%>
<%=name%>: <%=value%><br>
<%}
else{
%>
<%=name%>: <br><img src="imgs/<%=value%>" alt="" border="0"><br>
<%}
}
}%>
8. Directorios LDAP
Apache Directory Server
OpenLDAP
Novell eDirectory
Windows Server 2003 Active Directory
Oracle Internet Directory
Sun Java System Directory Server Enterprise Edition
9. Clientes LDAP
JXplorer (Java)
LDAP Browser/Editor (Java)
Luma (Unix)
Frood (Unix)
CoralDirectory LDAP Browser (Windows)
LDAP Exporter (Windows)
maX.500 Macintosh
phpLDAPadmin (web)