2008-12-23 17 views
63

Tengo una tarea simple de autenticar contra Active Directory utilizando Java. Solo verificando credenciales y nada más. Digamos que mi dominio es "fun.xyz.tld", la ruta OU es desconocida y el nombre de usuario/contraseña es testu/testp.Autenticación contra Active Directory con Java en Linux

Sé que hay algunas bibliotecas de Java que simplifican esta tarea, pero no tuve éxito en su implementación. La mayoría de los ejemplos que he encontrado abordan LDAP en general, no específicamente Active Directory. Emitir solicitud de LDAP significa enviar una ruta de acceso de unidad organizativa, que yo no tengo. Además, la aplicación que emite la solicitud LDAP ya debe estar vinculada a Active Directory para poder acceder a ella ... Inseguro, ya que las credenciales tendrían que almacenarse en algún lugar detectable. Me gustaría un enlace de prueba con credenciales de prueba, si es posible, esto significaría que la cuenta es válida.

Por último, si es posible, ¿hay alguna manera de cifrar ese mecanismo de autenticación? Sé que AD usa Kerberos, pero no estoy seguro de si los métodos LDAP de Java sí lo hacen.

¿Alguien tiene un ejemplo de código de trabajo? Gracias.

Respuesta

46

Aquí está el código que armé basado en el ejemplo de este blog: LINK y esta fuente: LINK.

import com.sun.jndi.ldap.LdapCtxFactory; 
import java.util.ArrayList; 
import java.util.Hashtable; 
import java.util.List; 
import java.util.Iterator; 
import javax.naming.Context; 
import javax.naming.AuthenticationException; 
import javax.naming.NamingEnumeration; 
import javax.naming.NamingException; 
import javax.naming.directory.Attribute; 
import javax.naming.directory.Attributes; 
import javax.naming.directory.DirContext; 
import javax.naming.directory.SearchControls; 
import javax.naming.directory.SearchResult; 
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE; 

class App2 { 

    public static void main(String[] args) { 

     if (args.length != 4 && args.length != 2) { 
      System.out.println("Purpose: authenticate user against Active Directory and list group membership."); 
      System.out.println("Usage: App2 <username> <password> <domain> <server>"); 
      System.out.println("Short usage: App2 <username> <password>"); 
      System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)"); 
      System.exit(1); 
     } 

     String domainName; 
     String serverName; 

     if (args.length == 4) { 
      domainName = args[2]; 
      serverName = args[3]; 
     } else { 
      domainName = "xyz.tld"; 
      serverName = "abc"; 
     } 

     String username = args[0]; 
     String password = args[1]; 

     System.out 
       .println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName); 

     // bind by using the specified username/password 
     Hashtable props = new Hashtable(); 
     String principalName = username + "@" + domainName; 
     props.put(Context.SECURITY_PRINCIPAL, principalName); 
     props.put(Context.SECURITY_CREDENTIALS, password); 
     DirContext context; 

     try { 
      context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props); 
      System.out.println("Authentication succeeded!"); 

      // locate this user's record 
      SearchControls controls = new SearchControls(); 
      controls.setSearchScope(SUBTREE_SCOPE); 
      NamingEnumeration<SearchResult> renum = context.search(toDC(domainName), 
        "(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls); 
      if (!renum.hasMore()) { 
       System.out.println("Cannot locate user information for " + username); 
       System.exit(1); 
      } 
      SearchResult result = renum.next(); 

      List<String> groups = new ArrayList<String>(); 
      Attribute memberOf = result.getAttributes().get("memberOf"); 
      if (memberOf != null) {// null if this user belongs to no group at all 
       for (int i = 0; i < memberOf.size(); i++) { 
        Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" }); 
        Attribute att = atts.get("CN"); 
        groups.add(att.get().toString()); 
       } 
      } 

      context.close(); 

      System.out.println(); 
      System.out.println("User belongs to: "); 
      Iterator ig = groups.iterator(); 
      while (ig.hasNext()) { 
       System.out.println(" " + ig.next()); 
      } 

     } catch (AuthenticationException a) { 
      System.out.println("Authentication failed: " + a); 
      System.exit(1); 
     } catch (NamingException e) { 
      System.out.println("Failed to bind to LDAP/get account information: " + e); 
      System.exit(1); 
     } 
    } 

    private static String toDC(String domainName) { 
     StringBuilder buf = new StringBuilder(); 
     for (String token : domainName.split("\\.")) { 
      if (token.length() == 0) 
       continue; // defensive check 
      if (buf.length() > 0) 
       buf.append(","); 
      buf.append("DC=").append(token); 
     } 
     return buf.toString(); 
    } 

} 
+3

'import com.sun.jndi.ldap.LdapCtxFactory;' - esto probablemente solo funcionará con Sun JVM. –

3

¿Está usted simplemente verificando las credenciales? En ese caso, puede hacer simplemente kerberos y no molestarse con LDAP.

+0

Sí, solo verificando las credenciales. Edité la pregunta con aclaración. ¿El código es diferente de la autenticación LDAP? –

6

Acabo de terminar un proyecto que utiliza AD y Java. Utilizamos Spring ldapTemplate.

AD es compatible con LDAP (casi), no creo que tenga ningún problema con la tarea que tiene. Me refiero al hecho de que es AD o cualquier otro servidor LDAP, no importa si solo quieres conectarte.

me gustaría echar un vistazo a: Spring LDAP

Tienen ejemplos también.

En cuanto al cifrado, utilizamos la conexión SSL (por lo que era LDAPS). AD tuvo que configurarse en un puerto/protocolo SSL.

Pero antes que nada, asegúrese de que puede conectarse correctamente a su AD a través de un IDE de LDAP. Yo uso Apache Directory Studio, es realmente genial, y está escrito en Java. Eso es todo lo que necesitaba. Para propósitos de prueba también puede instalar Apache Directory Server

+0

Luchiani, actualmente estoy desarrollando una aplicación web para la integración de java spring para compartir puntos (windows), mientras que no soy capaz de crear usuarios en Active Directory usando código java, ¿puedes compartir tu código para crear usuarios en el directorio activo con comentarios? para que pueda continuar mi trabajo a tiempo. –

90

Hay 3 protocolos de autenticación que se pueden utilizar para realizar la autenticación entre Java y Active Directory en Linux o cualquier otra plataforma (y estos no son sólo específico para los servicios HTTP):

  1. Kerberos - Kerberos proporciona Single Sign-On (SSO) y delegación, pero los servidores web también necesitan el soporte de SPNEGO para aceptar SSO a través de IE.

  2. NTLM - NTLM admite SSO a través de IE (y otros navegadores si están configurados correctamente).

  3. LDAP - Se puede usar un enlace LDAP para validar simplemente un nombre de cuenta y contraseña.

También hay algo que se llama "AD FS", que proporciona SSO para sitios web utilizando SAML que pone en la SSP de Windows por lo que en la práctica es básicamente una manera indirecta de la utilización de uno de los otros protocolos anteriores.

Cada protocolo tiene sus ventajas, pero como regla general, para obtener la máxima compatibilidad, generalmente debe intentar "hacer lo mismo que Windows". Entonces, ¿qué hace Windows? En primer lugar, la autenticación entre dos máquinas Windows favorece a Kerberos porque los servidores no necesitan comunicarse con el DC y los clientes pueden almacenar en caché los tickets Kerberos lo que reduce la carga en los DC (y porque Kerberos admite la delegación).

Pero si las partes que autentican no tienen cuentas de dominio o si el cliente no puede comunicarse con el DC, se requiere NTLM. Así que Kerberos y NTLM no son mutuamente excluyentes y NTLM no está obsoleto por Kerberos. De hecho, en cierto modo, NTLM es mejor que Kerberos. Tenga en cuenta que al mencionar Kerberos y NTLM al mismo tiempo, también debo mencionar SPENGO y la Autenticación Integrada de Windows (IWA). IWA es un término simple que básicamente significa Kerberos, NTLM o SPNEGO para negociar Kerberos o NTLM.

El uso de un enlace LDAP como forma de validar las credenciales no es eficiente y requiere SSL. Pero hasta hace poco la implementación de Kerberos y NTLM ha sido difícil, por lo que el uso de LDAP como un servicio de autenticación make-shift ha persistido. Pero en este punto, generalmente se debe evitar. LDAP es un directorio de información y no un servicio de autenticación. Úselo para su propósito previsto.

Entonces, ¿cómo implementar Kerberos o NTLM en Java y en el contexto de las aplicaciones web en particular?

Hay una serie de grandes empresas como Quest Software y Centrify que tienen soluciones que mencionan específicamente Java. Realmente no puedo comentar sobre estos ya que son "soluciones de gestión de identidad" para toda la compañía, por lo que, al mirar el giro de marketing en su sitio web, es difícil decir exactamente qué protocolos se usan y cómo. Debería ponerse en contacto con ellos para obtener más detalles.

La implementación de Kerberos en Java no es muy difícil ya que las bibliotecas estándar de Java son compatibles con Kerberos a través de las clases org.ietf.gssapi. Sin embargo, hasta hace poco ha habido un obstáculo importante: IE no envía tokens Kerberos brutos, sino que envía tokens SPNEGO. Pero con Java 6, se ha implementado SPNEGO. En teoría, debería poder escribir algún código GSSAPI que pueda autenticar a los clientes de IE. Pero no lo he intentado. La implementación de Sun de Kerberos ha sido una comedia de errores a lo largo de los años, por lo que, basado en el historial de Sun en esta área, no haría ninguna promesa sobre su implementación de SPENGO hasta que tenga ese ave en la mano.

Para NTLM, hay un proyecto de OSS gratuito llamado JCIFS que tiene un filtro de servlet de autenticación HTTP NTLM. Sin embargo, utiliza un método man-in-the-middle para validar las credenciales con un servidor SMB que no funciona con NTLMv2 (que lentamente se está convirtiendo en una política de seguridad de dominio requerida). Por ese motivo y otros, la parte del Filtro HTTP de JCIFS está programada para ser eliminada. Tenga en cuenta que hay un número de spin-offs que usan JCIFS para implementar la misma técnica. Por lo tanto, si ve otros proyectos que afirman ser compatibles con NTOS de NTLM, consulte la letra pequeña.

La única forma correcta de validar las credenciales NTLM con Active Directory es utilizar la llamada NetrLogonSamLogon DCERPC sobre NETLOGON con Secure Channel. ¿Existe tal cosa en Java? Sí.Aquí está:

http://www.ioplex.com/jespa.html

Jespa es una aplicación 100% Java que soporta NTLM NTLMv2, NTLMv1, opciones de integridad y confidencialidad completa y la validación de credenciales NETLOGON antes mencionado. E incluye un filtro HTTP SSO, un JAAS LoginModule, cliente HTTP, cliente y servidor SASL (con enlace JNDI), "proveedor de seguridad" genérico para crear servicios personalizados NTLM y más.

Mike

+3

Actualización: HttpClient ahora es compatible con Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO, esquemas de autenticación de Kerberos. –

2

Si todo lo que quiero hacer es autenticarse en AD con Kerberos, a continuación, un sencillo programa http://spnego.sourceforge.net/HelloKDC.java debe hacerlo.

Eche un vistazo a la documentación "previa al vuelo" del proyecto que habla sobre el programa HelloKDC.java.

4

Como ioplex y otros han dicho, hay muchas opciones. Para la autenticación mediante LDAP (y la API LDAP de Novell), he utilizado algo como:


LDAPConnection connection = new LDAPConnection(new LDAPJSSEStartTLSFactory()); 
connection.connect(hostname, port); 
connection.startTLS(); 
connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes()); 

Como una "particularidad", Active Directory permite a LDAP se une contra el "usuario @ dominio" sin utilizar el nombre completo de la cuenta. Este código usa StartTLS para habilitar el cifrado TLS en la conexión; la otra alternativa es LDAP sobre SSL, que no es compatible con my servidores AD.

El verdadero truco está en localizar el servidor y el host; la forma oficial es utilizar una búsqueda de registros DNS SRV (servicio) para localizar un paquete de hosts candidatos, luego hacer un "ping" de LDAP basado en UDP (en un formato particular de Microsoft) para ubicar el servidor correcto. Si está interesado, he publicado algunos blog articles sobre mi viaje de aventura y descubrimiento en esa área.

Si desea hacer la autenticación de nombre de usuario/contraseña basada en Kerberos, está viendo otra olla de pescado; es factible con el código Java GSS-API, aunque no estoy seguro de que realice el último paso para validar la autenticación. (El código que realiza la validación puede comunicarse con el servidor de AD para verificar el nombre de usuario y la contraseña, lo que da como resultado un ticket de otorgamiento de ticket para el usuario, pero para asegurarse de que el servidor AD no sea suplantado, también debe intentar obtener un ticket para el usuario en sí mismo, que es algo más complicado.)

Si desea realizar el inicio de sesión único basado en Kerberos, suponiendo que sus usuarios están autenticados en el dominio, puede hacerlo también con la API GSS de Java código. Publicaba una muestra de código, pero aún necesito convertir mi horrible prototipo en algo apto para ojos humanos. Consulte some code from SpringSource para obtener inspiración.

Si está buscando NTLM (que me dijeron que era menos seguro) o algo más, bueno, buena suerte.

+0

Entradas de blog muy útiles. ¡Gracias! – Aerse

+0

"usuario @ dominio" funciona muy bien, gracias. – chris

0

Le recomiendo que vea el paquete adbroker del proyecto oVirt. Utiliza Spring-Ldap y el módulo de inicio de sesión de Krb5 JAAS (con GSSAPI) para autenticarse utilizando Kerberos contra servidores Ldap (Active-Directory, ipa, rhds, Tivoli-DS).Busque el código en el motor \ backend \ manager \ modules \ bll \ src \ main \ java \ org \ ovirt \ engine \ core \ bll \ adbroker

Puede usar git para clonar el repositorio o navegar usando el enlace gerrit

Cuestiones relacionadas