2012-09-07 54 views
17

Necesito hacer Autenticación LDAP para una aplicación.Autenticación LDAP usando Java

He probado el siguiente programa:

import java.util.Hashtable; 

import javax.naming.Context; 
import javax.naming.NamingException; 
import javax.naming.ldap.InitialLdapContext; 
import javax.naming.ldap.LdapContext; 


public class LdapContextCreation { 
    public static void main(String[] args) { 
     LdapContextCreation ldapContxCrtn = new LdapContextCreation(); 
     LdapContext ctx = ldapContxCrtn.getLdapContext(); 
    } 
    public LdapContext getLdapContext(){ 
     LdapContext ctx = null; 
     try{ 
      Hashtable env = new Hashtable(); 
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
      env.put(Context.SECURITY_AUTHENTICATION, "Simple"); 
      //it can be <domain\\userid> something that you use for windows login 
      //it can also be 
      env.put(Context.SECURITY_PRINCIPAL, "[email protected]"); 
      env.put(Context.SECURITY_CREDENTIALS, "password"); 
      //in following property we specify ldap protocol and connection url. 
      //generally the port is 389 
      env.put(Context.PROVIDER_URL, "ldap://server.domain.com"); 
      ctx = new InitialLdapContext(env, null); 
      System.out.println("Connection Successful."); 
     }catch(NamingException nex){ 
      System.out.println("LDAP Connection: FAILED"); 
      nex.printStackTrace(); 
     } 
     return ctx; 
    } 

} 

Conseguir siguiente excepción:

LDAP Connection: FAILED javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials] at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3053) at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2999) at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2801) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2715) at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:305) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:187) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:205) at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:148) at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:78) at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:235) at javax.naming.InitialContext.initializeDefaultInitCtx(InitialContext.java:318) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:348) at javax.naming.InitialContext.internalInit(InitialContext.java:286) at javax.naming.InitialContext.init(InitialContext.java:308) at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:99) at LdapContextCreation.getLdapContext(LdapContextCreation.java:27) at LdapContextCreation.main(LdapContextCreation.java:12)

cuantos más puntos a considerar:

  1. Anteriormente yo estaba usando tomcat 5.3.5 pero alguien me dijo que solo Tomcat 6 sup lo conecta, así que descargué tomcat 6.0.35 y actualmente solo uso esta versión.

  2. Configurado server.xml y añade el siguiente código -

<Realm className="org.apache.catalina.realm.JNDIRealm" debug="99" connectionURL="ldap://server.domain.com:389/"
userPattern="{0}" />

  1. comentado el siguiente código de server.xml -

    <!-- Commenting for LDAP <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> -->

  2. Pasos 2 y 3 de article

  3. Alguien sugirió que hay algunos archivos jar que se supone que se va a copiar a Tomcat con el fin de ejecutar ldap autenticación, es que algo que tengo que hacer? ¿Y qué archivos jar?

  4. Además, estoy usando las credenciales correctas, ¿cuál es la causa del problema?

  5. ¿Hay alguna manera de averiguar los atributos correctos para LDAP en caso de que esté utilizando los incorrectos?

+0

Hay mejores bibliotecas para esto, pero aquí es una pregunta similar http://stackoverflow.com/a/12165647/1286621 Estoy de acuerdo con @jasim acerca del principal. Necesita averiguar qué formato está usando su servidor LDAP. Aquí hay un ejemplo de mi servidor de Active Directory "CN = bindUserName, CN = Users, DC = myDepartment, DC = myNetwork". La gente de LDAP debería poder decirle rápidamente cuál es el formato. También hay herramientas de GUI que pueden conectarse a LDAP y explorar los directorios. Habla primero con tus administradores. – Mike

+0

Solo un comentario más, ¿sabe usted que generalmente hay un usuario/contraseña "Enlace", uno que tiene permiso para buscar información en el Servidor LDAP? Una vez que enlaza al servidor, puede autenticar las credenciales de los usuarios. – Mike

Respuesta

6

Usted tendrá que proporcionar todo el DN de usuario en SECURITY_PRINCIPAL

como esto

 env.put(Context.SECURITY_PRINCIPAL, "cn=username,ou=testOu,o=test"); 
+0

No es cierto, depende de la implementación del servidor. –

+1

@ Michael-O Ciertamente es verdad. (1) Está en la especificación JNDI. (2) La autenticación LDAP se realiza a través de una operación LDAP 'bind', en todas las implementaciones del servidor LDAP. (3) La función de JNDI es superar las diferencias en la implementación del servidor. – EJP

+0

@EJP, estaba hablando de usar el DN solo para un proceso de vinculación. –

16

siguiente código autentica desde LDAP utilizando Java puro JNDI. El principio es: -

  1. Primero Buscar al usuario usando un administrador o usuario de DN.
  2. El objeto de usuario debe pasarse nuevamente a LDAP con la credencial de usuario
  3. Sin medios de excepción: autenticado con éxito. Else Autenticación Falló.

Fragmento de código

public static boolean authenticateJndi(String username, String password) throws Exception{ 
    Properties props = new Properties(); 
    props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
    props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT"); 
    props.put(Context.SECURITY_PRINCIPAL, "uid=adminuser,ou=special users,o=xx.com");//adminuser - User with special priviledge, dn user 
    props.put(Context.SECURITY_CREDENTIALS, "adminpassword");//dn user password 


    InitialDirContext context = new InitialDirContext(props); 

    SearchControls ctrls = new SearchControls(); 
    ctrls.setReturningAttributes(new String[] { "givenName", "sn","memberOf" }); 
    ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE); 

    NamingEnumeration<javax.naming.directory.SearchResult> answers = context.search("o=xx.com", "(uid=" + username + ")", ctrls); 
    javax.naming.directory.SearchResult result = answers.nextElement(); 

    String user = result.getNameInNamespace(); 

    try { 
     props = new Properties(); 
     props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
     props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT"); 
     props.put(Context.SECURITY_PRINCIPAL, user); 
     props.put(Context.SECURITY_CREDENTIALS, password); 

    context = new InitialDirContext(props); 
    } catch (Exception e) { 
     return false; 
    } 
    return true; 
} 
+0

Sé que esta entrada es un poco antigua, pero realmente tengo esta pregunta candente: ¿Por qué necesitamos un usuario administrador, por qué no podemos simplemente tratar de vincular con las credenciales de usuario que queremos autenticar en primer lugar? –

11

Ésta es mi aplicación de prueba LDAP de Java inicio de sesión LDAP apoyo: // y LDAPS: // certificado de prueba autofirmado. El código se toma de pocas publicaciones de SO, implementación simplificada y eliminación de sun.java heredado.* importaciones.

Uso
he ejecutar esto en máquinas Windows7 y Linux contra WinAD servicio de directorio. La aplicación imprime nombre de usuario y grupos de miembros.

$ java clases -cp test.LoginLDAP url = LDAP: //1.2.3.4: 389 [email protected] password = MyPwd

clases -cp $ java test.LoginLDAP url = ldaps: //1.2.3.4: 636 [email protected] password = MyPwd

aplicación de prueba es compatible con certificados de prueba con firma temporales para ldaps: // protocolo, este DummySSLFactory acepta cualquier servidor cert así el hombre -en el medio es posible. La instalación de la vida real debe importar el certificado del servidor a un archivo de almacén de claves JKS local y no usar la fábrica ficticia.

La aplicación utiliza el nombre de usuario y contraseña del usuario final para el contexto inicial y las consultas ldap, funciona para WinAD pero no sabe si se puede usar para todas las implementaciones del servidor ldap. Puede crear contexto con nombre de usuario interno + pwd luego ejecutar consultas para ver si se encuentra el usuario final dado.

LoginLDAP.java

package test; 

import java.util.*; 
import javax.naming.*; 
import javax.naming.directory.*; 

public class LoginLDAP { 

    public static void main(String[] args) throws Exception { 
     Map<String,String> params = createParams(args); 

     String url = params.get("url"); // ldap://1.2.3.4:389 or ldaps://1.2.3.4:636 
     String principalName = params.get("username"); // [email protected] 
     String domainName = params.get("domain"); // mydomain.com or empty 

     if (domainName==null || "".equals(domainName)) { 
      int delim = principalName.indexOf('@'); 
      domainName = principalName.substring(delim+1); 
     } 

     Properties props = new Properties(); 
     props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
     props.put(Context.PROVIDER_URL, url); 
     props.put(Context.SECURITY_PRINCIPAL, principalName); 
     props.put(Context.SECURITY_CREDENTIALS, params.get("password")); // secretpwd 
     if (url.toUpperCase().startsWith("LDAPS://")) { 
      props.put(Context.SECURITY_PROTOCOL, "ssl"); 
      props.put(Context.SECURITY_AUTHENTICATION, "simple"); 
      props.put("java.naming.ldap.factory.socket", "test.DummySSLSocketFactory");   
     } 

     InitialDirContext context = new InitialDirContext(props); 
     try { 
      SearchControls ctrls = new SearchControls(); 
      ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
      NamingEnumeration<SearchResult> results = context.search(toDC(domainName),"(& (userPrincipalName="+principalName+")(objectClass=user))", ctrls); 
      if(!results.hasMore()) 
       throw new AuthenticationException("Principal name not found"); 

      SearchResult result = results.next(); 
      System.out.println("distinguisedName: " + result.getNameInNamespace()); // CN=Firstname Lastname,OU=Mycity,DC=mydomain,DC=com 

      Attribute memberOf = result.getAttributes().get("memberOf"); 
      if(memberOf!=null) { 
       for(int idx=0; idx<memberOf.size(); idx++) { 
        System.out.println("memberOf: " + memberOf.get(idx).toString()); // CN=Mygroup,CN=Users,DC=mydomain,DC=com 
        //Attribute att = context.getAttributes(memberOf.get(idx).toString(), new String[]{"CN"}).get("CN"); 
        //System.out.println(att.get().toString()); // CN part of groupname 
       } 
      } 
     } finally { 
      try { context.close(); } catch(Exception ex) { } 
     }  
    } 

    /** 
    * Create "DC=sub,DC=mydomain,DC=com" string 
    * @param domainName sub.mydomain.com 
    * @return 
    */ 
    private static String toDC(String domainName) { 
     StringBuilder buf = new StringBuilder(); 
     for (String token : domainName.split("\\.")) { 
      if(token.length()==0) continue; 
      if(buf.length()>0) buf.append(","); 
      buf.append("DC=").append(token); 
     } 
     return buf.toString(); 
    } 

    private static Map<String,String> createParams(String[] args) { 
     Map<String,String> params = new HashMap<String,String>(); 
     for(String str : args) { 
      int delim = str.indexOf('='); 
      if (delim>0) params.put(str.substring(0, delim).trim(), str.substring(delim+1).trim()); 
      else if (delim==0) params.put("", str.substring(1).trim()); 
      else params.put(str, null); 
     } 
     return params; 
    } 

} 

y SSL clase de ayuda.

package test; 

import java.io.*; 
import java.net.*; 
import java.security.SecureRandom; 
import java.security.cert.X509Certificate;  
import javax.net.*; 
import javax.net.ssl.*; 

public class DummySSLSocketFactory extends SSLSocketFactory { 
    private SSLSocketFactory socketFactory; 
    public DummySSLSocketFactory() { 
     try { 
      SSLContext ctx = SSLContext.getInstance("TLS"); 
      ctx.init(null, new TrustManager[]{ new DummyTrustManager()}, new SecureRandom()); 
      socketFactory = ctx.getSocketFactory(); 
     } catch (Exception ex){ throw new IllegalArgumentException(ex); } 
    } 

     public static SocketFactory getDefault() { return new DummySSLSocketFactory(); } 

     @Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); } 
     @Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); } 

     @Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { 
     return socketFactory.createSocket(socket, string, i, bln); 
     } 
     @Override public Socket createSocket(String string, int i) throws IOException, UnknownHostException { 
     return socketFactory.createSocket(string, i); 
     } 
     @Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { 
     return socketFactory.createSocket(string, i, ia, i1); 
     } 
     @Override public Socket createSocket(InetAddress ia, int i) throws IOException { 
     return socketFactory.createSocket(ia, i); 
     } 
     @Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { 
     return socketFactory.createSocket(ia, i, ia1, i1); 
     } 
} 

class DummyTrustManager implements X509TrustManager { 
    @Override public void checkClientTrusted(X509Certificate[] xcs, String str) { 
     // do nothing 
    } 
    @Override public void checkServerTrusted(X509Certificate[] xcs, String str) { 
     /*System.out.println("checkServerTrusted for authType: " + str); // RSA 
     for(int idx=0; idx<xcs.length; idx++) { 
      X509Certificate cert = xcs[idx]; 
      System.out.println("X500Principal: " + cert.getSubjectX500Principal().getName()); 
     }*/ 
    } 
    @Override public X509Certificate[] getAcceptedIssuers() { 
     return new java.security.cert.X509Certificate[0]; 
    } 
} 
0

// Escrito por Mirza Qasim Ali.

// esta clase autenticará nombre de usuario LDAP o correo electrónico

// simplemente llaman LdapAuth.authenticateUserAndGetInfo (nombre de usuario, contraseña);

// Nota:. Configurar ldapURI, requiredAttributes, ADSearchPaths, accountSuffex

importación java.util *;

import javax.naming. *;

import java.util.regex. *;

import javax.naming.directory. *;

import javax.naming.ldap.InitialLdapContext;

import javax.naming.ldap.LdapContext;

public class {LdapAuth

private final static String ldapURI = "ldap://20.200.200.200:389/DC=corp,DC=local"; 

private final static String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; 

private static String[] requiredAttributes = {"cn","givenName","sn","displayName","userPrincipalName","sAMAccountName","objectSid","userAccountControl"}; 

// ver que activa usuario de directorio de OU hirarchy

cadena estática [] = ADSearchPaths privadas {

"OU=O365 Synced Accounts,OU=ALL USERS", 

    "OU=Users,OU=O365 Synced Accounts,OU=ALL USERS", 

    "OU=In-House,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS", 

    "OU=Torbram Users,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS", 

    "OU=Migrated Users,OU=TES-Users" 

};

private static String accountSuffex = "@corp.local"; // this will be used if user name is just provided 


private static void authenticateUserAndGetInfo (String user, String password) throws Exception { 

    try { 


     Hashtable<String,String> env = new Hashtable <String,String>(); 

     env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); 

     env.put(Context.PROVIDER_URL, ldapURI);  

     env.put(Context.SECURITY_AUTHENTICATION, "simple"); 

     env.put(Context.SECURITY_PRINCIPAL, user); 

     env.put(Context.SECURITY_CREDENTIALS, password); 

     DirContext ctx = new InitialDirContext(env); 

     String filter = "(sAMAccountName="+user+")"; // default for search filter username 

     if(user.contains("@")) // if user name is a email then 
      { 
       //String parts[] = user.split("\\@"); 
       //use different filter for email 
       filter = "(userPrincipalName="+user+")"; 

      } 

     SearchControls ctrl = new SearchControls(); 
     ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); 
     ctrl.setReturningAttributes(requiredAttributes); 

     NamingEnumeration userInfo = null; 


     Integer i = 0; 
     do 
     { 
      userInfo = ctx.search(ADSearchPaths[i], filter, ctrl); 

      i++; 
     }while(!userInfo.hasMore() && i < ADSearchPaths.length); 

     if (userInfo.hasMore()) { 

      SearchResult UserDetails = (SearchResult) userInfo.next(); 

Attributes userAttr = UserDetails.getAttributes();System.out.println("adEmail = "+userAttr.get("userPrincipalName").get(0).toString()); 

       System.out.println("adFirstName = "+userAttr.get("givenName").get(0).toString()); 

       System.out.println("adLastName = "+userAttr.get("sn").get(0).toString()); 

       System.out.println("name = "+userAttr.get("cn").get(0).toString()); 

       System.out.println("AdFullName = "+userAttr.get("cn").get(0).toString()); 

     } 

     userInfo.close(); 

    } 
    catch (javax.naming.AuthenticationException e) { 

    } 
} 

}

Cuestiones relacionadas