2012-09-07 16 views
17

Devo eseguire l'autenticazione LDAP per un'applicazione.Autenticazione LDAP con Java

ho provato il seguente programma:

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

} 

Ottenere seguente eccezione:

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)

qualche punto in più da considerare:

  1. All'inizio usavo tomcat 5.3.5 ma qualcuno mi ha detto che solo Tomcat 6 sup lo porto così ho scaricato tomcat 6.0.35 e attualmente utilizzo solo questa versione.

  2. Configurato server.xml e ha aggiunto il seguente codice -

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

  1. commentato il seguente codice da server.xml -

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

  2. Passi 2 e 3 da article

  3. Qualcuno ha suggerito che ci sono alcuni file jar che si suppone essere copiati tomcat al fine di eseguire l'autenticazione ldap, è che qualcosa che devo fare? E quali file jar?

  4. Inoltre, sto utilizzando le credenziali corrette di sicuro, quindi che cosa sta causando questo problema?

  5. C'è un modo per determinare gli attributi corretti per LDAP nel caso in cui io stia utilizzando quelli errati?

+0

Non ci sono librerie migliori di questo, ma qui è una domanda simile http://stackoverflow.com/a/12165647/1286621 Sono d'accordo con @jasim circa il principale. È necessario capire il formato utilizzato dal server LDAP. Ecco un esempio del mio server Active Directory "CN = bindUserName, CN = Users, DC = myDepartment, DC = myNetwork". Le persone LDAP dovrebbero essere abbastanza in grado di dirti quale sia il formato. Ci sono anche strumenti gui là fuori che possono connettersi a LDAP e sfogliare le directory. Parla prima con i tuoi amministratori. – Mike

+0

Solo un altro commento, sei consapevole del fatto che di solito c'è un utente/password "Bind", uno che ha il permesso di cercare le informazioni nel server LDAP corretto? Una volta eseguito il binding al server, è possibile autenticare le credenziali dell'utente. – Mike

risposta

6

Si dovrà fornire l'intera dn utente in SECURITY_PRINCIPAL

come questo

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

Non vero, dipende dall'implementazione del server. –

+1

@ Michael-O Sicuramente è vero. (1) È nella specifica JNDI. (2) L'autenticazione LDAP viene eseguita tramite un'operazione "bind" LDAP, su tutte le implementazioni del server LDAP. (3) È la funzione di JNDI per superare le differenze nell'implementazione del server. – EJP

+0

@EJP, stavo parlando dell'utilizzo del DN solo per un processo di bind. –

16

seguente codice autentica da LDAP usando soltanto Java JNDI. Il principio è: -

  1. Primo sguardo all'utente che utilizza un utente admin o DN.
  2. L'oggetto utente deve essere passato nuovamente a LDAP con le credenziali utente
  3. No Exception significa - Autenticato correttamente. Altra autenticazione fallita.

frammento di codice

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

So che questa voce è un po 'vecchia, ma ho davvero questa domanda scottante: perché abbiamo bisogno di un utente amministratore, perché non possiamo semplicemente provare a legare con le credenziali dell'utente che vogliamo autenticare in primo luogo? –

11

Questa è la mia applicazione di test LDAP login Java LDAP di supporto: // e LDAPS: // certificato di prova auto-firmato. Il codice è preso da pochi post SO, implementazione semplificata e rimosso legacy sun.java.* importazioni.

Uso
ho eseguito questo in macchine Windows7 e Linux contro il servizio di directory WinAD. L'applicazione stampa nome utente e gruppi di membri.

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

classi cp $ java test.LoginLDAP url = LDAPS: //1.2.3.4: 636 [email protected] password = MyPwd

applicazione test supporta i certificati di prova autofirmati temporanei per LDAPS: // protocollo, questo DummySSLFactory accetta qualsiasi server cert così l'uomo -in-the-middle è possibile. L'installazione in tempo reale dovrebbe importare il certificato del server in un file di archivio chiavi JKS locale e non utilizzare la fabbrica fittizia.

Applicazione utilizza il nome utente e la password di utente finale per il contesto iniziale e le query ldap, funziona per WinAD ma non si sa se può essere utilizzato per tutte le implementazioni del server LDAP. È possibile creare un contesto con nome utente interno + pwd quindi eseguire query per vedere se viene trovato un determinato utente finale.

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

} 

e SSL classe helper.

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

// Scritto da Mirza Qasim Ali.

// questa classe sarà l'autenticazione LDAP o email UserName

// semplicemente chiamano LdapAuth.authenticateUserAndGetInfo (nome utente, password);

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

import java.util *;

importazione javax.naming. *;

importazione java.util.regex. *;

importazione javax.naming.directory. *;

importazione javax.naming.ldap.InitialLdapContext;

importazione 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"}; 

// vederti di hirarchy

private static string [] = ADSearchPaths {

"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" 

} attiva OU utente directory;

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) { 

    } 
} 

}

Problemi correlati